起步软件技术论坛-X3

 找回密码
 立即注册
搜索
楼主: hero419

【结贴】3002版本中邮件转发附件内容会丢失。**

[复制链接]
发表于 2008-2-29 09:32:17 | 显示全部楼层
楼主,稍等,我们这边得测试一下
回复 支持 反对

使用道具 举报

发表于 2008-2-29 10:18:09 | 显示全部楼层
楼主,我测试了一下,没有出现你说的这种情况
回复 支持 反对

使用道具 举报

 楼主| 发表于 2008-2-29 13:50:49 | 显示全部楼层
哦,我是拿2827版本别人发给我的邮件做的转发测试。
如果是3002版本下别人发给我的邮件做转发,邮件大小就没有问题了,真奇怪啊。
这个不知道你们能不能测试下?
.                                                                                                                                                                                           .
.                                                                                                                                                                                           .
回复 支持 反对

使用道具 举报

 楼主| 发表于 2008-2-29 13:52:07 | 显示全部楼层
unit MAILEDITFORM;

interface

uses
  Business.System, Business.Forms, Business.Data,
  Business.System.SysUtils, Business.System.StringUtils,
  Borland.Delphi.Windows, Borland.Delphi.ShellAPI,
  Justep.Tools.Mail, Justep.Tools.Mail.DHTMLEDLib_TLB,
  CollaborationConsts;

type
  TMailEditor1AboutEvent = TNotifyEvent;
  TMailEditor1GetAccountNamesEvent = procedure (Sender: TObject; AccountNames: TStrings) of object;
  TMailEditor1GetOperUserInfoEvent = procedure (Sender: TObject;
    out AOperUserID, AOperUserDisplayName: string) of object;
  TMailEditor1AddressBookEvent = procedure (Sender: TObject; var SendTo, CC,
    BCC: string; var Changed: Boolean) of object;
  TMailEditor1SaveEvent = procedure (Sender: TObject; AStream: TStream;
    const APrepareSend: Boolean; var Saved: Boolean) of object;
  TMailEditor1SendingEvent = procedure (Sender: TObject;
    var CanSend: Boolean) of object;

  TMAILEDITFORM = class(TForm)
    cbrTools: TControlBar;
    tbrTools: TToolBar;
    pnlHeader: TPanel;
    btnSendTo: TButton;
    btnCC: TButton;
    lblSubject: TLabel;
    edtSendTo: TEdit;
    edtCC: TEdit;
    edtSubject: TEdit;
    msgMail: TMessage;
    miMainMenu: TMainMenu;
    imgTools: TImageList;
    miFile: TMenuItem;
    miOpen: TMenuItem;
    aclActions: TActionList;
    actOpen: TAction;
    actSave: TAction;
    actSaveAs: TAction;
    N1: TMenuItem;
    S1: TMenuItem;
    A1: TMenuItem;
    ToolButton2: TToolButton;
    actUnDo: TAction;
    miEdit: TMenuItem;
    miUnDo: TMenuItem;
    actCut: TAction;
    actCopy: TAction;
    actPaste: TAction;
    actReDo: TAction;
    R1: TMenuItem;
    N2: TMenuItem;
    T1: TMenuItem;
    C1: TMenuItem;
    P1: TMenuItem;
    actClear: TAction;
    N3: TMenuItem;
    A2: TMenuItem;
    actSelectAll: TAction;
    L1: TMenuItem;
    actFind: TAction;
    actFindNext: TAction;
    actReplace: TAction;
    N4: TMenuItem;
    F1: TMenuItem;
    X1: TMenuItem;
    E1: TMenuItem;
    ToolButton1: TToolButton;
    ToolButton3: TToolButton;
    ToolButton4: TToolButton;
    ToolButton5: TToolButton;
    ToolButton6: TToolButton;
    ToolButton7: TToolButton;
    tbrFormat: TToolBar;
    cbxFormatNames: TComboBox;
    cbxFontNames: TComboBox;
    cbxFontSizes: TComboBox;
    ToolButton8: TToolButton;
    btnTextColor: TColorToolButton;
    actFontColor: TAction;
    ToolButton10: TToolButton;
    miFormat: TMenuItem;
    miBlockFormatNames: TMenuItem;
    actFont: TAction;
    F2: TMenuItem;
    actFontBold: TAction;
    actFontItalic: TAction;
    actFontUnderLine: TAction;
    ToolButton11: TToolButton;
    ToolButton12: TToolButton;
    ToolButton13: TToolButton;
    actAlignmentLeft: TAction;
    actAlignmentRight: TAction;
    actAlignmentCenter: TAction;
    ToolButton14: TToolButton;
    ToolButton15: TToolButton;
    ToolButton16: TToolButton;
    ToolButton17: TToolButton;
    miAlignment: TMenuItem;
    N5: TMenuItem;
    N6: TMenuItem;
    N7: TMenuItem;
    actOrderList: TAction;
    actUnorderList: TAction;
    ToolButton18: TToolButton;
    ToolButton19: TToolButton;
    N8: TMenuItem;
    N9: TMenuItem;
    N10: TMenuItem;
    actIndent: TAction;
    actOutdent: TAction;
    ToolButton20: TToolButton;
    ToolButton21: TToolButton;
    actProperties: TAction;
    N11: TMenuItem;
    I1: TMenuItem;
    actBackColor: TAction;
    N12: TMenuItem;
    B1: TMenuItem;
    lvwAttachments: TListView;
    splAttachments: TSplitter;
    imgAttachSmall: TImageList;
    imgAttachLarge: TImageList;
    actNew: TAction;
    N13: TMenuItem;
    actClose: TAction;
    N14: TMenuItem;
    C2: TMenuItem;
    actNewAttachment: TAction;
    miInsert: TMenuItem;
    F3: TMenuItem;
    actDeleteAttachment: TAction;
    PopupMenu1: TPopupMenu;
    D1: TMenuItem;
    btnBCC: TButton;
    edtBCC: TEdit;
    edtFrom: TEdit;
    btnFrom: TButton;
    lblFrom: TLabel;
    lblSendTo: TLabel;
    lblCC: TLabel;
    lblBCC: TLabel;
    actHeaderVisible: TAction;
    miView: TMenuItem;
    N15: TMenuItem;
    actFromVisible: TAction;
    bvlHeader1: TBevel;
    bvlHeader2: TBevel;
    actBCCVisible: TAction;
    B2: TMenuItem;
    actPicture: TAction;
    N16: TMenuItem;
    P2: TMenuItem;
    iplMail: TInternetProtocol;
    actLine: TAction;
    L2: TMenuItem;
    actHyperLink: TAction;
    actRemoveHyperLink: TAction;
    H1: TMenuItem;
    R2: TMenuItem;
    popDHTMLEdit: TPopupMenu;
    T2: TMenuItem;
    C3: TMenuItem;
    P3: TMenuItem;
    N17: TMenuItem;
    L3: TMenuItem;
    N18: TMenuItem;
    N19: TMenuItem;
    A3: TMenuItem;
    N20: TMenuItem;
    N21: TMenuItem;
    N22: TMenuItem;
    N23: TMenuItem;
    N24: TMenuItem;
    N25: TMenuItem;
    N26: TMenuItem;
    N27: TMenuItem;
    N28: TMenuItem;
    I3: TMenuItem;
    ToolButton22: TToolButton;
    actPriorityHigh: TAction;
    actPriorityLow: TAction;
    ToolButton23: TToolButton;
    ToolButton24: TToolButton;
    ToolButton25: TToolButton;
    ToolButton26: TToolButton;
    ToolButton27: TToolButton;
    actPrint: TAction;
    N29: TMenuItem;
    P4: TMenuItem;
    ToolButton28: TToolButton;
    actToolbarNormalVisible: TAction;
    T3: TMenuItem;
    N30: TMenuItem;
    actToolBarFormatVisible: TAction;
    N31: TMenuItem;
    N32: TMenuItem;
    F4: TMenuItem;
    F5: TMenuItem;
    N33: TMenuItem;
    actOpenAttachment: TAction;
    actPrintAttachment: TAction;
    actSaveAsAttachment: TAction;
    A4: TMenuItem;
    O1: TMenuItem;
    P5: TMenuItem;
    actAddressBook: TAction;
    actSend: TAction;
    ToolButton29: TToolButton;
    ToolButton30: TToolButton;
    tbMenus: TToolBar;
    ToolButton31: TToolButton;
    ToolButton32: TToolButton;
    ToolButton33: TToolButton;
    ToolButton34: TToolButton;
    ToolButton35: TToolButton;
    ToolButton36: TToolButton;
    miHelp: TMenuItem;
    N35: TMenuItem;
    btnBGColor: TColorToolButton;
    PopupColors: TPopupPanel;
    Colors: TColorBox;
    N34: TMenuItem;
    actText: TAction;
    actHTML: TAction;
    T4: TMenuItem;
    HTMLH1: TMenuItem;
    actChangeFormat: TAction;
    pnlEdit: TPanel;
    actBlockFormat: TAction;
    ToolButton9: TToolButton;
    actAboutMail: TAction;
    ToolButton37: TToolButton;
    ToolButton38: TToolButton;
    pmAccountNames: TPopupMenu;
    actAccounts: TAction;
    actLaterSend: TAction;
    L4: TMenuItem;
    Action21: TMenuItem;
    actTransmit: TAction;
    ToolButton39: TToolButton;
    actReply: TAction;
    ToolButton40: TToolButton;
    N36: TMenuItem;
    actReply1: TMenuItem;
    N37: TMenuItem;
    ToolButton41: TToolButton;
    actCheckName: TAction;
    ToolButton42: TToolButton;
    miTools: TMenuItem;
    N39: TMenuItem;
    ToolButton43: TToolButton;
    procedure actOpenExecute(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure edtSubjectChange(Sender: TObject);
    procedure actSaveExecute(Sender: TObject);
    procedure actSaveAsExecute(Sender: TObject);
    procedure actUnDoExecute(Sender: TObject);
    procedure actUnDoUpdate(Sender: TObject);
    procedure actReDoUpdate(Sender: TObject);
    procedure actReDoExecute(Sender: TObject);
    procedure actCutUpdate(Sender: TObject);
    procedure actCopyExecute(Sender: TObject);
    procedure actCopyUpdate(Sender: TObject);
    procedure actPasteExecute(Sender: TObject);
    procedure actPasteUpdate(Sender: TObject);
    procedure actCutExecute(Sender: TObject);
    procedure actClearExecute(Sender: TObject);
    procedure actClearUpdate(Sender: TObject);
    procedure actSelectAllExecute(Sender: TObject);
    procedure actSelectAllUpdate(Sender: TObject);
    procedure actFindExecute(Sender: TObject);
    procedure actFindUpdate(Sender: TObject);
    procedure cbxFormatNamesChange(Sender: TObject);
    procedure cbxFontNamesChange(Sender: TObject);
    procedure cbxFontSizesChange(Sender: TObject);
    procedure actFontColorExecute(Sender: TObject);
    procedure actFontExecute(Sender: TObject);
    procedure actFontUpdate(Sender: TObject);
    procedure actFontBoldExecute(Sender: TObject);
    procedure actFontBoldUpdate(Sender: TObject);
    procedure actFontItalicExecute(Sender: TObject);
    procedure actFontItalicUpdate(Sender: TObject);
    procedure actFontUnderLineExecute(Sender: TObject);
    procedure actFontUnderLineUpdate(Sender: TObject);
    procedure actAlignmentLeftExecute(Sender: TObject);
    procedure actAlignmentLeftUpdate(Sender: TObject);
    procedure actAlignmentRightExecute(Sender: TObject);
    procedure actAlignmentRightUpdate(Sender: TObject);
    procedure actAlignmentCenterExecute(Sender: TObject);
    procedure actAlignmentCenterUpdate(Sender: TObject);
    procedure actOrderListExecute(Sender: TObject);
    procedure actOrderListUpdate(Sender: TObject);
    procedure actUnorderListExecute(Sender: TObject);
    procedure actUnorderListUpdate(Sender: TObject);
    procedure actIndentExecute(Sender: TObject);
    procedure actIndentUpdate(Sender: TObject);
    procedure actOutdentExecute(Sender: TObject);
    procedure actOutdentUpdate(Sender: TObject);
    procedure actPropertiesExecute(Sender: TObject);
    procedure actPropertiesUpdate(Sender: TObject);
    procedure actBackColorExecute(Sender: TObject);
    procedure actBackColorUpdate(Sender: TObject);
    procedure edtSendToChange(Sender: TObject);
    procedure edtCCChange(Sender: TObject);
    procedure actSaveUpdate(Sender: TObject);
    procedure actNewExecute(Sender: TObject);
    procedure actCloseExecute(Sender: TObject);
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    procedure FormDestroy(Sender: TObject);
    procedure actNewAttachmentExecute(Sender: TObject);
    procedure actDeleteAttachmentExecute(Sender: TObject);
    procedure actDeleteAttachmentUpdate(Sender: TObject);
    procedure pnlHeaderResize(Sender: TObject);
    procedure actNewAttachmentUpdate(Sender: TObject);
    procedure pnlHeaderCanResize(Sender: TObject; var NewWidth,
      NewHeight: Integer; var Resize: Boolean);
    procedure actHeaderVisibleUpdate(Sender: TObject);
    procedure actHeaderVisibleExecute(Sender: TObject);
    procedure actFromVisibleExecute(Sender: TObject);
    procedure actFromVisibleUpdate(Sender: TObject);
    procedure actBCCVisibleExecute(Sender: TObject);
    procedure actBCCVisibleUpdate(Sender: TObject);
    procedure actPictureExecute(Sender: TObject);
    procedure actPictureUpdate(Sender: TObject);
    procedure iplMailStart(Sender: TObject; const Url: String;
      var Stream: TStream);
    procedure actLineExecute(Sender: TObject);
    procedure actHyperLinkExecute(Sender: TObject);
    procedure actHyperLinkUpdate(Sender: TObject);
    procedure actRemoveHyperLinkExecute(Sender: TObject);
    procedure actRemoveHyperLinkUpdate(Sender: TObject);
    procedure actPriorityHighExecute(Sender: TObject);
    procedure actPriorityHighUpdate(Sender: TObject);
    procedure actPriorityLowExecute(Sender: TObject);
    procedure actPriorityLowUpdate(Sender: TObject);
    procedure actPrintExecute(Sender: TObject);
    procedure actToolbarNormalVisibleExecute(Sender: TObject);
    procedure actToolbarNormalVisibleUpdate(Sender: TObject);
    procedure actToolBarFormatVisibleExecute(Sender: TObject);
    procedure actToolBarFormatVisibleUpdate(Sender: TObject);
    procedure actLineUpdate(Sender: TObject);
    procedure actNewUpdate(Sender: TObject);
    procedure actOpenUpdate(Sender: TObject);
    procedure actSaveAsAttachmentExecute(Sender: TObject);
    procedure actSaveAsAttachmentUpdate(Sender: TObject);
    procedure actOpenAttachmentExecute(Sender: TObject);
    procedure actOpenAttachmentUpdate(Sender: TObject);
    procedure actPrintAttachmentExecute(Sender: TObject);
    procedure actPrintAttachmentUpdate(Sender: TObject);
    procedure lvwAttachmentsDblClick(Sender: TObject);
    procedure actAddressBookExecute(Sender: TObject);
    procedure btnSendToClick(Sender: TObject);
    procedure actSendExecute(Sender: TObject);
    procedure actSendUpdate(Sender: TObject);
    procedure actAddressBookUpdate(Sender: TObject);
    procedure FormActivate(Sender: TObject);
    procedure cbrToolsBandMove(Sender: TObject; Control: TControl;
      var ARect: TRect);
    procedure ColorsChange(Sender: TObject);
    procedure ColorsClick(Sender: TObject);
    procedure lvwAttachmentsKeyDown(Sender: TObject; var Key: Word;
      Shift: TShiftState);
    procedure actChangeFormatExecute(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure actTextUpdate(Sender: TObject);
    procedure actHTMLUpdate(Sender: TObject);
    procedure actFontColorUpdate(Sender: TObject);
    procedure actBlockFormatExecute(Sender: TObject);
    procedure actBlockFormatUpdate(Sender: TObject);
    procedure cbrToolsResize(Sender: TObject);
    procedure actAboutMailExecute(Sender: TObject);
    procedure pmAccountNamesPopup(Sender: TObject);
    procedure actAccountsExecute(Sender: TObject);
    procedure actAccountsUpdate(Sender: TObject);
    procedure actLaterSendExecute(Sender: TObject);
    procedure actLaterSendUpdate(Sender: TObject);
    procedure actTransmitExecute(Sender: TObject);
    procedure actTransmitUpdate(Sender: TObject);
    procedure actReplyExecute(Sender: TObject);
    procedure actCheckNameExecute(Sender: TObject);
    procedure actCheckNameUpdate(Sender: TObject);
  private
    FMailEditor: TCustomMailEditor;

    FAccountNames: TStrings;

    FDHTMLEdit: TDHTMLEdit;
    FTextEdit: TRichEdit;

    FAttachments: TStrings;
    FFileName: string;
    FModified: Boolean;
    FReadOnly: Boolean;
    FIsTransmitMail: Boolean;
    FTransmitMailGUID: string;


    FOnNew: TNotifyEvent;
    FOnSave: TMailEditor1SaveEvent;
    FOnSent: TNotifyEvent;
    FOnSending: TMailEditor1SendingEvent;
    FOnGetAccountNames: TMailEditor1GetAccountNamesEvent;
    FOnGetOperUserInfo: TMailEditor1GetOperUserInfoEvent;
    FOnAddressBook: TMailEditor1AddressBookEvent;
    FOnAbout: TMailEditor1AboutEvent;
    FMailPath: string;
    FMailGUID: String;
    FReplayHeader: String;

    procedure GetAttachmentContent(AAttachment: TMessageItem; AStream: TStream);

    procedure DoDHTMLEditDocumentComplete(Sender: TObject);
    procedure DoDHTMLEditDisplayChanged(Sender: TObject);
    procedure DoDHTMLEditShowContextMenu(Sender: TObject; xPos,
      yPos: Integer);
    procedure DoTextEditChange(Sender: TObject);

    procedure DoSave(const APrepareSend: Boolean);

    function CheckSaved: Boolean;
    function DecodeURL(const S: string): string;
    function EncodeURL(const S: string): string;
//    function GetBackColor: TColor;
    function GetBlockFormatName: WideString;
    function GetFontName: WideString;
    function GetFontSize: Integer;
//    function GetForeColor: TColor;
    procedure BlockFormatNameMenuClick(Sender: TObject);
    procedure ClearDHTMLEditDirty;
    procedure DecodeURLs;
    procedure DefaultSave;
    procedure EncodeURLs;
    procedure GetBlockFormatNames(AFormats: TStrings);
    procedure RefreshAttachs;
    procedure RefreshMessage;
    procedure RefreshAttachmentIndex;
    procedure SetActionState(AAction: TAction; CommandID: Integer);
//    procedure SetBackColor(AColor: TColor);
    procedure SetBlockFormatName(const AFormatName: WideString);
    procedure SetFontName(const AFontName: WideString);
    procedure SetFontSize(AFontSize: Integer);
//    procedure SetForeColor(AColor: TColor);
    procedure SetModified;
    procedure SetReadOnly(Value: Boolean);
    procedure ShowAttachments;

    procedure CreateTextEdit;
    procedure CreateHTMLEdit;
    procedure FreeAllEdit;

    procedure DoRefreshAccountNames;
    procedure DoGetAccountNames(AccountNames: TStrings);

    procedure DoAddAccountNamesToMenu;
    procedure DoRefreshMenuByAccountNames;

    procedure SaveHTML(AStream: TStream);
    procedure SaveText(AStream: TStream);

    procedure AccountNameMenuItemClick(Sender: TObject);
    function DoGetMailFrom: string;

    function CanSend: Boolean;

    procedure CheckName;
    procedure LoadAttachments;
  protected
    function FormatIsTextType: Boolean;
    function FormatIsHTMLType: Boolean;

    procedure RefreshEdit;
    procedure RefreshToolBarWithContentType;

    procedure GetAccountNames(AccountNames: TStrings);  virtual;

    property DHTMLEdit: TDHTMLEdit read FDHTMLEdit;
    property TextEdit: TRichEdit read FTextEdit;
  public
    constructor Create(AContext: Business.Model.TContext; AMailEditor: TCustomMailEditor);

    function HasAttachments: Boolean;
    function Modified: Boolean;
    procedure LoadFromFile(const AFileName: string);
    procedure LoadFromStream(AStream: TStream);
    procedure SaveToFile(const AFileName: string);
    procedure SaveToStream(AStream: TStream);
    function ColorToHtml(Color: TColor): string;
    property Replayheader: String read FReplayHeader write FReplayHeader;
    property Attachments: TStrings read FAttachments;
    property FileName: string read FFileName;
    property MailPath: string read FMailPath write FMailPath;
    property MailGUID: String read FMailGUID write FMailGUID;

//  published
    property OnAddressBook: TMailEditor1AddressBookEvent read FOnAddressBook
      write FOnAddressBook;
    property OnAbout: TMailEditor1AboutEvent read FOnAbout write FOnAbout;
    property OnGetAccountNames: TMailEditor1GetAccountNamesEvent
      read FOnGetAccountNames write FOnGetAccountNames;
    property OnGetOperUserInfo: TMailEditor1GetOperUserInfoEvent
      read FOnGetOperUserInfo write FOnGetOperUserInfo;
    property OnNew: TNotifyEvent read FOnNew write FOnNew;
    property OnSave: TMailEditor1SaveEvent read FOnSave write FOnSave;
    property OnSending: TMailEditor1SendingEvent read FOnSending write FOnSending;
    property OnSent: TNotifyEvent read FOnSent write FOnSent;
    property ReadOnly: Boolean read FReadOnly write SetReadOnly;
    property TransmitMailGUID: String read FTransmitMailGUID write FTransmitMailGUID;
    property IsTransmitMail: Boolean read FIsTransmitMail write FIsTransmitMail;
  end;

//var
//  MailEditorForm: TMailEditor1;

implementation

{$R *.DFM}

const
  sMailHost = '://mail/';

var
  iMailIPID: Integer = 0;

function GetFileType(const AFileName: string): string;
var
  reg: TRegistry;
begin
  Result := SysUtils.EmptyStr;
  reg := TRegistry.Create;
  try
    reg.RootKey := HKEY_CLASSES_ROOT;
    if not reg.OpenKey(ExtractFileExt(AFileName), False) then Exit;
    Result := reg.ReadString('');
  finally
    reg.Free;
  end;
end;

function GetFileContentType(const AFileName: string): string;
var
  reg: TRegistry;
begin
  Result := EmptyStr;
  reg := TRegistry.Create;
  try
    reg.RootKey := HKEY_CLASSES_ROOT;
    if not reg.OpenKey(ExtractFileExt(AFileName), False) then Exit;
    Result := reg.ReadString('Content Type');
  finally
    reg.Free;
  end;
end;

procedure GetFileIcon(const AFileName: string; AIcon: TIcon);
var
  H, I: Integer;
  S: string;
  reg: TRegistry;
begin
  S := GetFileType(AFileName);
  reg := TRegistry.Create;
  try
    reg.RootKey := HKEY_CLASSES_ROOT;
    if (S <> '') and reg.OpenKey(S + '\DefaultIcon', False) then S := reg.ReadString('');
    if S = '' then S := 'Shell32.dll';
    I := Pos(',', S);
    if I > 0 then
    begin
      H := StrToInt(Copy(S, I + 1, Length(S) - I));
      S := Copy(S, 1, I - 1);
      I := H;
    end
    else
      I := 0;
    AIcon.Handle := ExtractIcon(MailExtCtrls.GetHInstance, PChar(S), I);
  finally
    reg.Free;
  end;
end;

function HtmlToColor(Color: WideString): TColor;
var
  i, j, StrLength: Integer;
  lChar: Char;
  lBit: Byte;
  Power: Longint;
begin
  Result := 0;
  try
    StrLength := Length(Color);
    for i := 2 to  StrLength do
    begin
      lChar := UpperCase(Char(Color))[0];
      lBit := Byte(lChar);
      case lBit of
        65 .. 70:  lBit := lBit - 55;
        48 .. 57:  lBit := lBit - 48;
        else       lBit := 0;
      end;
      Power := 1;
      for j := 3 to i do Power := Power * 16;
      Result := Result + lBit * Power;
    end;
  except
    Result := 0;
  end;
end;

function ColorToHtml(Color: TColor): WideString;
begin
  result:='#' + IntToHex(Color and $FF,2)+
          IntToHex(Color shr 8 and $FF,2)+
          IntToHex(Color shr 16 and $FF,2);
end;

function TMAILEDITFORM.CheckSaved: Boolean;
begin
  if Modified then
    case MessageBox(Handle, '邮件已经被修改, 是否保存?', '是否保存', MB_ICONQUESTION or MB_YESNOCANCEL) of
      ID_YES:
      begin
        DoSave(False);
        Result := True;
      end;
      ID_NO: Result := True;
    else
      Result := False;
    end
  else
    Result := True;
end;

constructor TMailEditForm.Create(AContext: Business.Model.TContext; AMailEditor: TCustomMailEditor);
begin
  inherited Create(AContext);
  FMailEditor := AMailEditor;
end;

function TMAILEDITFORM.DecodeURL(const S: string): string;
var
  I: Integer;
begin
  Result := iplMail.Protocol + sMailHost;
  I := Length(Result);
  if CompareText(Copy(S, 1, I), Result) = 0 then
    Result := Copy(S, I + 1, Length(S))
  else
    Result := S;
end;

function TMAILEDITFORM.EncodeURL(const S: string): string;
begin
  if (CompareText('http://', Copy(S, 1, 7)) = 0) or
    (CompareText('file://', Copy(S, 1, 7)) = 0) or
    (CompareText('ftp://', Copy(S, 1, 6)) = 0) then
    Result := S
  else
    Result := iplMail.Protocol + sMailHost + S;
end;

{function TMailEditor1.GetBackColor: TColor;
begin
  Result := HtmlToColor(DHTMLEdit.ExecCommand(DECMD_GETBACKCOLOR, OLECMDEXECOPT_DONTPROMPTUSER));
end;}

function TMAILEDITFORM.GetBlockFormatName: WideString;
begin
  Result := ObjectHelper.ToString(DHTMLEdit.ExecCommand(DECMD_GETBLOCKFMT, OLECMDEXECOPT_DONTPROMPTUSER));
end;

function TMAILEDITFORM.GetFontName: WideString;
var
  V: OleVariant;
begin
  V := DHTMLEdit.ExecCommand(DECMD_GETFONTNAME, OLECMDEXECOPT_DONTPROMPTUSER);
  if Variants.VarIsNull(V) or Variants.VarIsEmpty(V) then
    Result := ''
  else
    Result := ObjectHelper.ToString(V);
end;

function TMAILEDITFORM.GetFontSize: Integer;
var
  V: OleVariant;
begin
  V := DHTMLEdit.ExecCommand(DECMD_GETFONTSIZE, OLECMDEXECOPT_DONTPROMPTUSER);
  if Variants.VarIsNull(V) or Variants.VarIsEmpty(V) then
    Result := -1
  else
    Result := ObjectHelper.ToInt(V) - 1;
end;

{function TMailEditor1.GetForeColor: TColor;
begin
  Result := HtmlToColor(DHTMLEdit.ExecCommand(DECMD_GETFORECOLOR, OLECMDEXECOPT_DONTPROMPTUSER));
end;}

procedure TMAILEDITFORM.BlockFormatNameMenuClick(Sender: TObject);
begin
  SetBlockFormatName((Sender as TMenuItem).Caption);
end;

procedure TMAILEDITFORM.ClearDHTMLEditDirty;
var
  lFileName: string;
  V: OleVariant;
begin
  //为了把 IsDirty 清除,不得不采用这种变态做法。
  if DHTMLEdit.IsDirty then
  begin
    lFileName := jsSysUtils.RequestTempFile(jsSysUtils.GetSystemTempPath, '~TMP', 0);
    V := lFileName;
    DHTMLEdit.SaveDocument(V);
  end;
end;

procedure TMAILEDITFORM.DecodeURLs;
var
  I: Integer;
  iImage: IHTMLImgElement;
begin
  for I := 0 to DHTMLEdit.DOM.images.length - 1 do
  begin
    iImage := DHTMLEdit.DOM.images.Item(I, 0) as IHTMLImgElement;
    iImage.src := DecodeURL(iImage.src);
  end;
end;

procedure TMAILEDITFORM.DefaultSave;
begin
  if FileName = '' then
    actSaveAs.Execute
  else
    SaveToFile(FileName);
end;

procedure TMAILEDITFORM.EncodeURLs;
var
  I: Integer;
  iImage: IHTMLImgElement;
begin
  for I := 0 to DHTMLEdit.DOM.images.length - 1 do
  begin
    iImage := DHTMLEdit.DOM.images.Item(I, 0) as IHTMLImgElement;
    iImage.src := EncodeURL(iImage.src);
  end;
end;

procedure TMAILEDITFORM.GetBlockFormatNames(AFormats: TStrings);
begin
  MailExtCtrls.GetHTMLEditBlockFormatNames(DHTMLEdit, AFormats);
end;

procedure TMAILEDITFORM.RefreshAttachs;
var
  I: Integer;
  lListItem: TListItem;
  lIcon: TIcon;
begin
  Attachments.Clear;
  msgMail.GetAttachFileNames(Attachments);
  if Attachments.Count > 0 then
  begin
    lvwAttachments.Items.BeginUpdate;
    lIcon := TIcon.Create;
    try
      lvwAttachments.Items.Clear;
      imgAttachSmall.Clear;
      for I := 0 to Attachments.Count - 1 do
      begin
        lListItem := lvwAttachments.Items.Add;
        lListItem.Caption := Attachments[I];
        GetFileIcon(Attachments[I], lIcon);
        lListItem.ImageIndex := imgAttachLarge.AddIcon(lIcon);
        lListItem.Data := Attachments.Objects[I];
        imgAttachSmall.AddIcon(lIcon);
      end;
    finally
      lIcon.Free;
      lvwAttachments.Items.EndUpdate;
    end;
    lvwAttachments.Selected := lvwAttachments.Items[0];
  end
  else begin
    lvwAttachments.Items.Clear;
    imgAttachSmall.Clear;
  end;
  ShowAttachments;
end;

procedure TMAILEDITFORM.RefreshMessage;
var
  S: WideString;
begin
  edtFrom.Text := msgMail.From;
  edtSendTo.Text := msgMail.SendTo;
  edtCC.Text := msgMail.CC;
  edtBCC.Text := msgMail.BCC;
  edtSubject.Text := msgMail.Subject;
  S := Trim(msgMail.Body.Text);
  if FormatIsTextType then  //增加回复,转发时的原邮件体
    TextEdit.Text := StrUtils.AnsiReplaceText(FReplayHeader, '<br>', #13) + S
  else if FormatIsHTMLType then
    if (S = '') and (FReplayHeader = '') then
      DHTMLEdit.NewDocument
    else
    begin  // 这里曾经试图取到Dom的OutText那样会好一点儿但失败了目前只好用这样的方法。
      S := StrUtils.AnsiReplaceText(S, '<body>', '<body>' + FReplayHeader);
      if Pos(FReplayHeader, S) <= 0 then
        S := StrUtils.AnsiReplaceText(S, '<html>', '<html>' + FReplayHeader);
      if Pos(FReplayHeader, S) <= 0 then
        S := FReplayHeader + S;
      DHTMLEdit.DocumentHTML := S;
    end;

  RefreshAttachs;
  FModified := False;
end;

procedure TMAILEDITFORM.RefreshAttachmentIndex;
var
  I: Integer;
  lAttachments: TStringList;
  lMessageItem: TMessageItem;
begin
  lAttachments := TStringList.Create;
  try
    msgMail.GetAttachFileNames(lAttachments);
    for I := 0 to lAttachments.count - 1 do
    begin
      lMessageItem := lAttachments.Objects[I] as TMessageItem;
      // 这里初始化附件索引号,供分布加载使用
      lMessageItem.AttachmentIndex := I + 1;
    end;
  finally
    lAttachments.Free;
  end;
end;

procedure TMAILEDITFORM.SetActionState(AAction: TAction; CommandID: Integer);
begin
  if FormatIsTextType then
    AAction.Enabled := False
  else if FormatIsHTMLType then
  begin
    AAction.Enabled := not ReadOnly and (DHTMLEdit.QueryStatus(CommandID) in [DECMDF_LATCHED, DECMDF_ENABLED]);
    AAction.Checked := DHTMLEdit.QueryStatus(CommandID) = DECMDF_LATCHED;
  end;
end;

{procedure TMailEditor1.SetBackColor(AColor: TColor);
var
  V: OleVariant;
begin
  V := ColorToHtml(AColor);
  DHTMLEdit.ExecCommand(DECMD_SETBACKCOLOR, OLECMDEXECOPT_DODEFAULT, V);
end;}

procedure TMAILEDITFORM.SetBlockFormatName(const AFormatName: WideString);
var
  V: OleVariant;
begin
  V := AFormatName;
  DHTMLEdit.ExecCommand(DECMD_SETBLOCKFMT, OLECMDEXECOPT_DODEFAULT, V);
end;

procedure TMAILEDITFORM.SetFontName(const AFontName: WideString);
var
  V: OleVariant;
begin
  V := AFontName;
  DHTMLEdit.ExecCommand(DECMD_SETFONTNAME, OLECMDEXECOPT_DODEFAULT, V);
end;

procedure TMAILEDITFORM.SetFontSize(AFontSize: Integer);
var
  V: OleVariant;
begin
  V := AFontSize + 1;
  DHTMLEdit.ExecCommand(DECMD_SETFONTSIZE, OLECMDEXECOPT_DODEFAULT, V);
end;

{procedure TMailEditor1.SetForeColor(AColor: TColor);
var
  V: OleVariant;
begin
  V := ColorToHtml(AColor);
  DHTMLEdit.ExecCommand(DECMD_SETFORECOLOR, OLECMDEXECOPT_DODEFAULT, V);
end;}

procedure TMAILEDITFORM.SetModified;
begin
  FModified := True;
end;

procedure TMAILEDITFORM.SetReadOnly(Value: Boolean);
begin
  FReadOnly := Value;
  lblFrom.Visible := ReadOnly and edtFrom.Visible;
  lblSendTo.Visible := ReadOnly;
  lblCC.Visible := ReadOnly;
  lblBCC.Visible := ReadOnly and edtBCC.Visible;
  btnFrom.Visible := not ReadOnly and edtFrom.Visible;
  btnSendTo.Visible := not ReadOnly;
  btnCC.Visible := not ReadOnly;
  btnBCC.Visible := not ReadOnly and edtBCC.Visible;
  edtFrom.ReadOnly := ReadOnly;
  edtSendTo.ReadOnly := ReadOnly;
  edtCC.ReadOnly := ReadOnly;
  edtBCC.ReadOnly := ReadOnly;
  edtSubject.ReadOnly := ReadOnly;
  cbxFormatNames.Enabled := not ReadOnly;
  cbxFontNames.Enabled := not ReadOnly;
  cbxFontSizes.Enabled := not ReadOnly;
  miBlockFormatNames.Enabled := not ReadOnly;

  if FormatIsHTMLType then
    DHTMLEdit.BrowseMode := ReadOnly
  else if FormatIsTextType then
    TextEdit.ReadOnly := ReadOnly;
end;

procedure TMAILEDITFORM.ShowAttachments;
begin
  if HasAttachments then
  begin
    lvwAttachments.Visible := True;
    splAttachments.Visible := True;
  end
  else begin
    splAttachments.Visible := False;
    lvwAttachments.Visible := False;
  end;
end;

procedure TMAILEDITFORM.CreateTextEdit;
begin
  FTextEdit := TRichEdit.Create(nil);
  FTextEdit.ScrollBars := TScrollstyle.ssBoth;
  with FTextEdit do
  begin
    FTextEdit.ScrollBars := TScrollstyle.ssBoth;
    Parent := pnlEdit;
    Align := TAlign.alClient;
    OnChange := DoTextEditChange;
    ReadOnly := Self.ReadOnly;
  end;
end;

procedure TMAILEDITFORM.CreateHTMLEdit;
begin
  FDHTMLEdit := TDHTMLEdit.Create(nil);
  with FDHTMLEdit do
  begin
    Parent := pnlEdit;
    Align := TAlign.alClient;
    BrowseMode := ReadOnly;
    ActivateApplets := True;
    ActivateActiveXControls := True;
    OnDocumentComplete := DoDHTMLEditDocumentComplete;
    OnDisplayChanged := DoDHTMLEditDisplayChanged;
    OnShowContextMenu := DoDHTMLEditShowContextMenu;
  end;
end;

procedure TMAILEDITFORM.FreeAllEdit;
begin
  if Assigned(FDHTMLEdit) then
  begin
    FDHTMLEdit.Parent := nil;
    FDHTMLEdit.Free;
    FDHTMLEdit := nil;
  end;

  if Assigned(FTextEdit) then
  begin
    FTextEdit.Parent := nil;
    FTextEdit.Free;
    FTextEdit := nil;
  end;
end;

procedure TMAILEDITFORM.DoRefreshAccountNames;
begin
  if FAccountNames.Count = 0 then
    GetAccountNames(FAccountNames);
end;

procedure TMAILEDITFORM.DoGetAccountNames(AccountNames: TStrings);
begin
  if Assigned(OnGetAccountNames) then
    OnGetAccountNames(Self, AccountNames);
end;

procedure TMAILEDITFORM.DoAddAccountNamesToMenu;
var
  I: Integer;
  lMenuItem: TMenuItem;
begin
  for I := 0 to FAccountNames.Count - 1 do
  begin
    lMenuItem := TMenuItem.Create(nil);
    lMenuItem.Caption := FAccountNames[I];
    lMenuItem.OnClick := AccountNameMenuItemClick;
    lMenuItem.RadioItem := True;
    pmAccountNames.Items.Add(lMenuItem);
  end;
end;

procedure TMAILEDITFORM.DoRefreshMenuByAccountNames;
var
  I: Integer;
begin
if pmAccountNames.Items.Count = 0 then
    DoAddAccountNamesToMenu;

  for I := 0 to pmAccountNames.Items.Count - 1 do
    with pmAccountNames.Items[I] do
      Checked := Pos(Caption, msgMail.From) <> 0;

  if (msgMail.From = '') and (pmAccountNames.Items.Count > 0) then
    pmAccountNames.Items[0].Checked := True;
end;

procedure TMAILEDITFORM.SaveHTML(AStream: TStream);
var
  I: Integer;
  S: string;
  lStream: TStream;
  iImage: IHTMLImgElement;
  lMessageItem: TMessageItem;
begin
  for I := 0 to DHTMLEdit.DOM.images.length - 1 do
  begin
    iImage := DHTMLEdit.DOM.images.Item(I, 0) as IHTMLImgElement;
    S := iImage.src;
    if (CompareText(Copy(S, 1, 5), 'file:') = 0) or (Copy(S, 2, 2) = ':\') then
    begin
      if CompareText(Copy(S, 1, 5), 'file:') = 0 then
      begin
        Delete(S, 1, 5);
        while S[1] = '/' do Delete(S, 1, 1);
      end;
      lStream := TFileStream.Create(S, fmOpenRead);
      try
        lMessageItem := msgMail.Message.Add;
        lMessageItem.SetContent(lStream);
        lMessageItem.ContentType := GetFileContentType(S);
        lMessageItem.ContentName := S;
        lMessageItem.ContentID := InetMsg.CreateMailID;
        iImage.src := EncodeUrl('CID:' + lMessageItem.ContentID);
      finally
        lStream.Free;
      end;
    end;
  end;
  DecodeUrls;
  msgMail.Body.Text := DHTMLEdit.DocumentHTML;
  EncodeUrls;
  msgMail.SaveToStream(AStream);
  ClearDHTMLEditDirty;
end;

procedure TMAILEDITFORM.SaveText(AStream: TStream);
begin
  msgMail.Body.Text := TextEdit.Text;
  msgMail.SaveToStream(AStream);
  TextEdit.Modified := False;
end;

procedure TMAILEDITFORM.AccountNameMenuItemClick(Sender: TObject);
begin
  TMenuItem(Sender).Checked := True;
  msgMail.From := DoGetMailFrom;
end;

function TMAILEDITFORM.DoGetMailFrom: string;
var
  I: Integer;
  lOperUserID, lOperUserDisplayName: string;
  lQuery: TQuery;
begin
  Result := '';
  if pmAccountNames.Items.Count = 0 then
  begin
    if Assigned(OnGetOperUserInfo) then
      OnGetOperUserInfo(Self, lOperUserID, lOperUserDisplayName);
    Result := Format('"%s" <%s>', [lOperUserDisplayName, lOperUserID]);
  end
  else
    for I := 0 to pmAccountNames.Items.Count - 1 do
      if pmAccountNames.Items[I].Checked then
      begin
        lQuery := TQuery.Create(nil);
        try
          lQuery.ConnectionString := TRTLConsts.CollaborationDatabaseConnectionString;
          lQuery.CommandText := 'SELECT  FOWNERGUID FROM TMAILACCOUNTS WHERE FMAILADDRESS = ''' + pmAccountNames.Items[I].Caption + '''';
          lQuery.Open;
          Result := lQuery.FieldByName('FOWNERGUID').AsString;
        finally
          lQuery.Free;
        end;
       end;
end;

function TMAILEDITFORM.FormatIsTextType: Boolean;
begin
  Result := msgMail.ContentFormatType = TContentFormatType.ftText;
end;

function TMAILEDITFORM.FormatIsHTMLType: Boolean;
begin
  Result := msgMail.ContentFormatType = TContentFormatType.ftHTML;
end;

procedure TMAILEDITFORM.RefreshEdit;
begin
  LockWindowUpdate(Handle);
  try
    FreeAllEdit;

    case msgMail.ContentFormatType of
      TContentFormatType.ftText : CreateTextEdit;
      TContentFormatType.ftHTML : CreateHTMLEdit;
    end;
    RefreshToolBarWithContentType;
  finally
    LockWindowUpdate(0);
  end;
end;

procedure TMAILEDITFORM.RefreshToolBarWithContentType;
var
  lVis: Boolean;
begin
  lVis := msgMail.ContentFormatType = TContentFormatType.ftHTML;
  tbrFormat.Visible := lVis;
  tbrFormat.Perform(Business.Forms.Controls.CM_VISIBLECHANGED, Ord(lVis), 0);
  cbxFormatNames.Enabled := lVis;
  cbxFontNames.Enabled := lVis;
  cbxFontSizes.Enabled := lVis;
end;

procedure TMAILEDITFORM.GetAccountNames(AccountNames: TStrings);
begin
  DoGetAccountNames(AccountNames);
end;

function TMAILEDITFORM.HasAttachments: Boolean;
begin
  Result := Attachments.Count > 0;
end;

function TMAILEDITFORM.Modified: Boolean;
begin
  Result := False;
  if FormatIsHTMLType then
    Result := FModified or DHTMLEdit.Busy
    //FModified or DHTMLEdit.Busy//IsDirty
  else if FormatIsTextType then
    Result := (FModified or TextEdit.Modified) and not TextEdit.ReadOnly;
end;

procedure TMAILEDITFORM.LoadFromFile(const AFileName: string);
var
  lStream: TStream;
begin
  lStream := TFileStream.Create(AFileName, fmOpenRead);
  try
    LoadFromStream(lStream);
    FFileName := AFileName;
  finally
    lStream.Free;
  end;
end;

procedure TMAILEDITFORM.LoadFromStream(AStream: TStream);
begin
  msgMail.LoadFromStream(AStream);
  RefreshEdit;
  RefreshMessage;
  RefreshAttachmentIndex;
end;

procedure TMAILEDITFORM.SaveToFile(const AFileName: string);
var
  lStream: TStream;
begin
  lStream := TFileStream.Create(AFileName, Classes.fmCreate);
  try
    SaveToStream(lStream);
    FFileName := AFileName;
  finally
    lStream.Free;
  end;
end;

procedure TMAILEDITFORM.SaveToStream(AStream: TStream);
begin
  msgMail.From := DoGetMailFrom;
  msgMail.Subject := edtSubject.Text;

  CheckName;
  msgMail.SendTo := edtSendTo.Text;
  msgMail.CC := edtCC.Text;
  msgMail.Bcc := edtBcc.Text;

  case msgMail.ContentFormatType of
    TContentFormatType.ftText : SaveText(AStream);
    TContentFormatType.ftHTML : SaveHTML(AStream);
  end;

  FModified := False;
end;

procedure TMAILEDITFORM.FormCreate(Sender: TObject);
begin
  IsTransmitMail := False;
  TransmitMailGUID := '';//在原来的基础上增加这两段
  Desktop := True;
  Inc(iMailIPID);
  iplMail.Protocol := iplMail.Protocol + IntToStr(iMailIPID);
  iplMail.Active := True;
  FAttachments := TStringList.Create;
  cbxFontNames.Items := Forms.Screen.Fonts;

  FAccountNames := TStringList.Create;
end;

procedure TMAILEDITFORM.FormDestroy(Sender: TObject);
begin
  FreeAllEdit;
  FAttachments.Free;
  FAccountNames.Free;
end;

procedure TMAILEDITFORM.FormCloseQuery(Sender: TObject;
  var CanClose: Boolean);
begin
  CanClose := CheckSaved;
end;

procedure TMAILEDITFORM.actNewExecute(Sender: TObject);
begin
  if Assigned(OnNew) then
    OnNew(Self)
  else
    if CheckSaved then
    begin
      FFileName := '';
      msgMail.Clear;
      RefreshMessage;
    end;
end;

procedure TMAILEDITFORM.actOpenExecute(Sender: TObject);
var
  lOpenDialog: TMailOpenDialog;
begin
  if not CheckSaved then Exit;
  lOpenDialog := TMailOpenDialog.Create(Self);
  try
    lOpenDialog.Filter := '电子邮件文件(*.eml)|*.eml|所有文件(*.*)|*.*';
    lOpenDialog.DefaultExt := 'eml';
    if lOpenDialog.Execute then LoadFromFile(lOpenDialog.FileName);
    BringWindowToTop(Handle);
  finally
    lOpenDialog.Free;
  end;
end;

procedure TMAILEDITFORM.actCloseExecute(Sender: TObject);
begin
  Close;
end;

procedure TMAILEDITFORM.edtSubjectChange(Sender: TObject);
begin
  Caption := edtSubject.Text;
  SetModified;
end;

procedure TMAILEDITFORM.actSaveExecute(Sender: TObject);
begin
  DoSave(False);
end;

procedure TMAILEDITFORM.DoSave(const APrepareSend: Boolean);
var
  lStream: TStream;
  lSaved: Boolean;
begin
  if Assigned(OnSave) then
  begin
    lSaved := False;
    lStream := THugeMemoryStream.Create(0);
    try
      LoadAttachments; // 现在存储结构不好,加载实现了分布加载,存储这里没有做到没修改的不保存
      SaveToStream(lStream);
      lStream.Position := 0;
      OnSave(Self, lStream, APrepareSend, lSaved);
      FModified := not lSaved;
    finally
      lStream.Free;
    end;
  end
  else
    DefaultSave;
end;

procedure TMAILEDITFORM.actSaveAsExecute(Sender: TObject);
var
  lSaveDialog: TMailSaveDialog;
begin
  lSaveDialog := TMailSaveDialog.Create(Self);
  try
    Borland.Delphi.Windows.SetParent(lSaveDialog.Handle, Handle);
    lSaveDialog.Filter := '电子邮件文件(*.eml)|*.eml|所有文件(*.*)|*.*';
    lSaveDialog.DefaultExt := 'eml';
    lSaveDialog.Options := lSaveDialog.Options + [TOpenOption.ofOverwritePrompt];
    if FileName = '' then
      lSaveDialog.FileName := '新的邮件'
    else
      lSaveDialog.FileName := FileName;
    if lSaveDialog.Execute then SaveToFile(lSaveDialog.FileName);
    BringWindowToTop(Handle);
  finally
    lSaveDialog.Free;
  end;
end;

procedure TMAILEDITFORM.actUnDoExecute(Sender: TObject);
begin
  if FormatIsTextType then
    TextEdit.Undo
  else if FormatIsHTMLType then
    DHTMLEdit.ExecCommand(DECMD_UNDO, OLECMDEXECOPT_DODEFAULT);
end;

procedure TMAILEDITFORM.actUnDoUpdate(Sender: TObject);
begin
  if FormatIsHTMLType then
    SetActionState(Sender as TAction, DECMD_UNDO)
  else if FormatIsTextType then
    TAction(Sender).Enabled := TextEdit.CanUndo;
end;

procedure TMAILEDITFORM.actReDoUpdate(Sender: TObject);
begin
  SetActionState(Sender as TAction, DECMD_REDO);
end;

procedure TMAILEDITFORM.actReDoExecute(Sender: TObject);
begin
  DHTMLEdit.ExecCommand(DECMD_REDO, OLECMDEXECOPT_DODEFAULT);
end;

procedure TMAILEDITFORM.actCutUpdate(Sender: TObject);
begin
  if FormatIsHTMLType then
    SetActionState(Sender as TAction, DECMD_CUT)
  else if FormatIsTextType then
    TAction(Sender).Enabled := TextEdit.SelLength > 0;
end;

procedure TMAILEDITFORM.actCutExecute(Sender: TObject);
begin
  if FormatIsTextType then
    TextEdit.CutToClipboard
  else if FormatIsHTMLType then
    DHTMLEdit.ExecCommand(DECMD_CUT, OLECMDEXECOPT_DODEFAULT);
end;

procedure TMAILEDITFORM.actCopyExecute(Sender: TObject);
begin
  if FormatIsTextType then
    TextEdit.CopyToClipboard
  else if FormatIsHTMLType then
    DHTMLEdit.ExecCommand(DECMD_COPY, OLECMDEXECOPT_DODEFAULT);
end;

procedure TMAILEDITFORM.actCopyUpdate(Sender: TObject);
begin
  if FormatIsHTMLType then
    SetActionState(Sender as TAction, DECMD_COPY)
  else if FormatIsTextType then
    TAction(Sender).Enabled := TextEdit.SelLength <> 0;
end;

procedure TMAILEDITFORM.actPasteExecute(Sender: TObject);
begin
  if FormatIsTextType then
    TextEdit.PasteFromClipboard
  else if FormatIsHTMLType then
    DHTMLEdit.ExecCommand(DECMD_PASTE, OLECMDEXECOPT_DODEFAULT);
end;

procedure TMAILEDITFORM.actPasteUpdate(Sender: TObject);
begin
  if FormatIsHTMLType then
    SetActionState(Sender as TAction, DECMD_PASTE)
  else if FormatIsTextType then
    TAction(Sender).Enabled := True;
end;

procedure TMAILEDITFORM.actClearExecute(Sender: TObject);
begin
  if FormatIsTextType then
    TAction(Sender).Enabled := TextEdit.SelLength > 0
  else if FormatIsHTMLType then
    DHTMLEdit.ExecCommand(DECMD_DELETE, OLECMDEXECOPT_DODEFAULT);
end;

procedure TMAILEDITFORM.actClearUpdate(Sender: TObject);
begin
  SetActionState(Sender as TAction, DECMD_DELETE);
end;

procedure TMAILEDITFORM.actSelectAllExecute(Sender: TObject);
begin
  if FormatIsTextType then
    TextEdit.SelectAll
  else if FormatIsHTMLType then
    DHTMLEdit.ExecCommand(DECMD_SELECTALL, OLECMDEXECOPT_DODEFAULT);
end;

procedure TMAILEDITFORM.actSelectAllUpdate(Sender: TObject);
begin
  if FormatIsTextType then
    TAction(Sender).Enabled := TextEdit.Text <> ''
  else if FormatIsHTMLType then
    SetActionState(Sender as TAction, DECMD_SELECTALL);
end;

procedure TMAILEDITFORM.actFindExecute(Sender: TObject);
begin
  if FormatIsHTMLType then
    DHTMLEdit.ExecCommand(DECMD_FINDTEXT, OLECMDEXECOPT_DODEFAULT);
end;

procedure TMAILEDITFORM.actFindUpdate(Sender: TObject);
begin
  SetActionState(Sender as TAction, DECMD_FINDTEXT);
end;

procedure TMAILEDITFORM.GetAttachmentContent(AAttachment: TMessageItem; AStream: TStream);
var
  I: Integer;
  lListItem: TListItem;
  lIcon: TIcon;
begin
  Attachments.Clear;
  msgMail.GetAttachFileNames(Attachments);
end;

procedure TMAILEDITFORM.DoDHTMLEditDocumentComplete(Sender: TObject);
var
  I: Integer;
  lMenuItem: TMenuItem;
begin
  cbxFormatNames.Items.Clear;
  GetBlockFormatNames(cbxFormatNames.Items);
  miBlockFormatNames.Clear;
  for I := 0 to cbxFormatNames.Items.Count - 1 do
  begin
    lMenuItem := TMenuItem.Create(miBlockFormatNames);
    lMenuItem.AutoHotkeys := TMenuItemAutoFlag.maManual;
    lMenuItem.AutoLineReduction := TMenuItemAutoFlag.maManual;
    lMenuItem.Caption := cbxFormatNames.Items[I];
    lMenuItem.OnClick := BlockFormatNameMenuClick;
    lMenuItem.RadioItem := True;
    miBlockFormatNames.Add(lMenuItem);
  end;
  EncodeUrls;
  ClearDHTMLEditDirty;
end;

procedure TMAILEDITFORM.DoDHTMLEditDisplayChanged(Sender: TObject);
var
  State: DHTMLEDITCMDF;
begin
  cbxFormatNames.ItemIndex := cbxFormatNames.Items.IndexOf(GetBlockFormatName);
  cbxFontNames.ItemIndex := cbxFontNames.Items.IndexOf(GetFontName);
  cbxFontSizes.ItemIndex := GetFontSize;

  State := DHTMLEdit.QueryStatus(DECMD_GETFORECOLOR);
  btnTextColor.Enabled := True;
  if State = DECMDF_DISABLED then
    btnTextColor.Enabled := False
  else
    btnTextColor.Color := HTMLToColor(ObjectHelper.ToString(DHTMLEdit.ExecCommand(DECMD_GETFORECOLOR, OLECMDEXECOPT_DONTPROMPTUSER)));

  State := DHTMLEdit.QueryStatus(DECMD_GETBACKCOLOR);
  btnBGColor.Enabled := True;
  if State = DECMDF_DISABLED then
    btnBGColor.Enabled := False
  else
    btnBGColor.Color := HTMLToColor(ObjectHelper.ToString(DHTMLEdit.ExecCommand(DECMD_GETBACKCOLOR, OLECMDEXECOPT_DONTPROMPTUSER)));
end;

procedure TMAILEDITFORM.DoDHTMLEditShowContextMenu(Sender: TObject; xPos,
  yPos: Integer);
var
  pt: TPoint;
begin
  GetCursorPos(pt);
  popDHTMLEdit.Popup(pt.X, pt.Y);
end;

procedure TMAILEDITFORM.DoTextEditChange(Sender: TObject);
begin
  FModified := True;
end;

procedure TMAILEDITFORM.cbxFormatNamesChange(Sender: TObject);
begin
  SetBlockFormatName(cbxFormatNames.Text);
end;

procedure TMAILEDITFORM.cbxFontNamesChange(Sender: TObject);
begin
  SetFontName(cbxFontNames.Text);
end;

procedure TMAILEDITFORM.cbxFontSizesChange(Sender: TObject);
begin
  SetFontSize(cbxFontSizes.ItemIndex);
end;

procedure TMAILEDITFORM.actFontColorExecute(Sender: TObject);
begin
  Colors.ActiveColor := TColorToolButton(Sender).Color;
  PopupColors.Popup(btnTextColor, TAlignment.taLeftJustify);
end;

procedure TMAILEDITFORM.actFontColorUpdate(Sender: TObject);
begin
  TAction(Sender).Enabled := not FormatIsTextType and not ReadOnly;
end;

procedure TMAILEDITFORM.actFontExecute(Sender: TObject);
begin
  DHTMLEdit.ExecCommand(DECMD_FONT, OLECMDEXECOPT_DODEFAULT);
end;

procedure TMAILEDITFORM.actFontUpdate(Sender: TObject);
begin
  SetActionState(Sender as TAction, DECMD_FONT);
end;

procedure TMAILEDITFORM.actFontBoldExecute(Sender: TObject);
begin
  DHTMLEdit.ExecCommand(DECMD_BOLD, OLECMDEXECOPT_DODEFAULT);
end;

procedure TMAILEDITFORM.actFontBoldUpdate(Sender: TObject);
begin
  SetActionState(Sender as TAction, DECMD_BOLD);
end;

procedure TMAILEDITFORM.actFontItalicExecute(Sender: TObject);
begin
  DHTMLEdit.ExecCommand(DECMD_ITALIC, OLECMDEXECOPT_DODEFAULT);
end;

procedure TMAILEDITFORM.actFontItalicUpdate(Sender: TObject);
begin
  SetActionState(Sender as TAction, DECMD_ITALIC);
end;

procedure TMAILEDITFORM.actFontUnderLineExecute(Sender: TObject);
begin
  DHTMLEdit.ExecCommand(DECMD_UNDERLINE, OLECMDEXECOPT_DODEFAULT);
end;

procedure TMAILEDITFORM.actFontUnderLineUpdate(Sender: TObject);
begin
  SetActionState(Sender as TAction, DECMD_UNDERLINE);
end;

procedure TMAILEDITFORM.actAlignmentLeftExecute(Sender: TObject);
begin
  DHTMLEdit.ExecCommand(DECMD_JUSTIFYLEFT, OLECMDEXECOPT_DODEFAULT);
end;

procedure TMAILEDITFORM.actAlignmentLeftUpdate(Sender: TObject);
begin
  SetActionState(Sender as TAction, DECMD_JUSTIFYLEFT);
end;

procedure TMAILEDITFORM.actAlignmentRightExecute(Sender: TObject);
begin
  DHTMLEdit.ExecCommand(DECMD_JUSTIFYRIGHT, OLECMDEXECOPT_DODEFAULT);
end;

procedure TMAILEDITFORM.actAlignmentRightUpdate(Sender: TObject);
begin
  SetActionState(Sender as TAction, DECMD_JUSTIFYRIGHT);
end;

procedure TMAILEDITFORM.actAlignmentCenterExecute(Sender: TObject);
begin
  DHTMLEdit.ExecCommand(DECMD_JUSTIFYCENTER, OLECMDEXECOPT_DODEFAULT);
end;

procedure TMAILEDITFORM.actAlignmentCenterUpdate(Sender: TObject);
begin
  SetActionState(Sender as TAction, DECMD_JUSTIFYCENTER);
end;

procedure TMAILEDITFORM.actOrderListExecute(Sender: TObject);
begin
  DHTMLEdit.ExecCommand(DECMD_ORDERLIST, OLECMDEXECOPT_DODEFAULT);
end;

procedure TMAILEDITFORM.actOrderListUpdate(Sender: TObject);
begin
  SetActionState(Sender as TAction, DECMD_ORDERLIST)
end;

procedure TMAILEDITFORM.actUnorderListExecute(Sender: TObject);
begin
  DHTMLEdit.ExecCommand(DECMD_UNORDERLIST, OLECMDEXECOPT_DODEFAULT);
end;

procedure TMAILEDITFORM.actUnorderListUpdate(Sender: TObject);
begin
  SetActionState(Sender as TAction, DECMD_UNORDERLIST);
end;

procedure TMAILEDITFORM.actIndentExecute(Sender: TObject);
begin
  DHTMLEdit.ExecCommand(DECMD_INDENT, OLECMDEXECOPT_DODEFAULT);
end;

procedure TMAILEDITFORM.actIndentUpdate(Sender: TObject);
begin
  SetActionState(Sender as TAction, DECMD_INDENT);
end;

procedure TMAILEDITFORM.actOutdentExecute(Sender: TObject);
begin
  DHTMLEdit.ExecCommand(DECMD_OUTDENT, OLECMDEXECOPT_DODEFAULT);
end;

procedure TMAILEDITFORM.actOutdentUpdate(Sender: TObject);
begin
  SetActionState(Sender as TAction, DECMD_OUTDENT);
end;

procedure TMAILEDITFORM.actPropertiesExecute(Sender: TObject);
begin
  DHTMLEdit.ExecCommand(DECMD_PROPERTIES, OLECMDEXECOPT_DODEFAULT);
end;

procedure TMAILEDITFORM.actPropertiesUpdate(Sender: TObject);
begin
  SetActionState(Sender as TAction, DECMD_PROPERTIES);
end;

procedure TMAILEDITFORM.actBackColorExecute(Sender: TObject);
//!!! 陶松修改  为了解决颜色框的位置问题
//!!! 旧代码
{var
  lColorDialog: TMailColorDialog;
begin
  lColorDialog := TMailColorDialog.Create(Self);
  try
    lColorDialog.Color := GetBackColor;
    if lColorDialog.Execute then SetBackColor(lColorDialog.Color);
    BringWindowToTop(Handle);
  finally
    lColorDialog.Free;
  end;}
//!!! 新代码
begin
  Colors.ActiveColor := TColorToolButton(Sender).Color;
  PopupColors.Popup(btnBGColor, TAlignment.taLeftJustify);
//修改结束
end;

procedure TMAILEDITFORM.actBackColorUpdate(Sender: TObject);
begin
  if FormatIsHTMLType then
    TAction(Sender).Enabled := not ReadOnly
  else if FormatIsTextType then
    TAction(Sender).Enabled := False;
end;

procedure TMAILEDITFORM.edtSendToChange(Sender: TObject);
begin
  SetModified;
end;

procedure TMAILEDITFORM.edtCCChange(Sender: TObject);
begin
  SetModified;
end;

procedure TMAILEDITFORM.actSaveUpdate(Sender: TObject);
begin
  actSave.Enabled := Modified and (not readonly);
end;

procedure TMAILEDITFORM.actNewAttachmentExecute(Sender: TObject);
var
  I: Integer;
  lOpenDialog: TMailOpenDialog;
begin
  lOpenDialog := TMailOpenDialog.Create(Self);
  try
    lOpenDialog.Filter := '所有文件(*.*)|*.*';
    lOpenDialog.Options := lOpenDialog.Options + [TOpenOption.ofAllowMultiSelect, TOpenOption.ofFileMustExist];
    if lOpenDialog.Execute then
    begin
      for I := 0 to lOpenDialog.Files.Count - 1 do
        msgMail.AddAttachment(lOpenDialog.Files[I]);
      SetModified;
      RefreshAttachs;
    end;
    BringWindowToTop(Handle);

    // todo 以下两个过程,解决98下不能刷新的问题。
    if Assigned(FTextEdit) then
      FTextEdit.Refresh;
    if Assigned(FDHTMLEdit) then
      FDHTMLEdit.Refresh;
  finally
    lOpenDialog.Free;
  end;
end;

procedure TMAILEDITFORM.actDeleteAttachmentExecute(Sender: TObject);
var
  I: Integer;
begin
  for I := 0 to lvwAttachments.Items.Count - 1 do
    if lvwAttachments.Items[I].Selected then
      TObject(lvwAttachments.Items[I].Data).Free;
  SetModified;
  RefreshAttachs;
end;

procedure TMAILEDITFORM.actDeleteAttachmentUpdate(Sender: TObject);
begin
  actDeleteAttachment.Enabled := not ReadOnly and (lvwAttachments.SelCount > 0);
end;

procedure TMAILEDITFORM.pnlHeaderCanResize(Sender: TObject; var NewWidth,
  NewHeight: Integer; var Resize: Boolean);
begin
  Resize := True;
  NewHeight := 68;
  if edtFrom.Visible then NewHeight := NewHeight + (edtFrom.Height + 4);
  if edtBCC.Visible then NewHeight := NewHeight + (edtBCC.Height + 4);
end;

procedure TMAILEDITFORM.pnlHeaderResize(Sender: TObject);
var
  I: Integer;
begin
  edtFrom.Width := pnlHeader.ClientWidth - edtFrom.Left - 8;
  edtSendTo.Width := edtFrom.Width;
  edtCC.Width := edtFrom.Width;
  edtBCC.Width := edtFrom.Width;
  edtSubject.Width := edtFrom.Width;
  I := 0;
  if edtFrom.Visible then
  begin
    lblFrom.Top := I;
    btnFrom.Top := I;
    edtFrom.Top := I;
    Inc(I, edtFrom.Height + 4);
  end;
  if edtSendTo.Visible then
  begin
    lblSendTo.Top := I;
    btnSendTo.Top := I;
    edtSendTo.Top := I;
    Inc(I, edtSendTo.Height + 4);
  end;
  if edtCC.Visible then
  begin
    lblCC.Top := I;
    btnCC.Top := I;
    edtCC.Top := I;
    Inc(I, edtCC.Height + 4);
  end;
  if edtBCC.Visible then
  begin
    lblBCC.Top := I;
    btnBCC.Top := I;
    edtBCC.Top := I;
    Inc(I, edtBCC.Height + 4);
  end;
  if edtSubject.Visible then
  begin
    lblSubject.Top := I;
    edtSubject.Top := I;
  end;
end;

procedure TMAILEDITFORM.actNewAttachmentUpdate(Sender: TObject);
begin
  actNewAttachment.Enabled := not ReadOnly;
end;

procedure TMAILEDITFORM.actHeaderVisibleUpdate(Sender: TObject);
begin
  actHeaderVisible.Checked := pnlHeader.Visible;
end;

procedure TMAILEDITFORM.actHeaderVisibleExecute(Sender: TObject);
begin
  bvlHeader2.Visible := not pnlHeader.Visible;
  pnlHeader.Visible := not pnlHeader.Visible;
end;

procedure TMAILEDITFORM.actFromVisibleExecute(Sender: TObject);
begin
  edtFrom.Visible := not edtFrom.Visible;
  lblFrom.Visible := ReadOnly and edtFrom.Visible;
  btnFrom.Visible := not ReadOnly and edtFrom.Visible;
end;

procedure TMAILEDITFORM.actFromVisibleUpdate(Sender: TObject);
begin
  actFromVisible.Checked := edtFrom.Visible;
end;

procedure TMAILEDITFORM.actBCCVisibleExecute(Sender: TObject);
begin
  edtBCC.Visible := not edtBCC.Visible;
  lblBCC.Visible := ReadOnly and edtBCC.Visible;
  btnBCC.Visible := not ReadOnly and edtBCC.Visible;
end;

procedure TMAILEDITFORM.actBCCVisibleUpdate(Sender: TObject);
begin
  actBCCVisible.Checked := edtBCC.Visible;
  actBCCVisible.Enabled := not ReadOnly;
end;

procedure TMAILEDITFORM.actPictureExecute(Sender: TObject);
begin
  DHTMLEdit.ExecCommand(DECMD_IMAGE, OLECMDEXECOPT_DODEFAULT);
end;

procedure TMAILEDITFORM.actPictureUpdate(Sender: TObject);
begin
  SetActionState(Sender as TAction, DECMD_IMAGE);
end;

procedure TMAILEDITFORM.iplMailStart(Sender: TObject; const Url: String;
  var Stream: TStream);
var
  mi: TMessageItem;
  S: string;
begin
  S := DecodeUrl(Url);
  if CompareText(Copy(S, 1, 4), 'CID:') = 0 then
    mi := msgMail.FindByContentID(Copy(S, 5, Length(S) - 4))
  else
    mi := msgMail.Find(S);
  if Assigned(mi) then
  begin
    Stream := THugeMemoryStream.Create(0);
    mi.GetContent(Stream);
    Stream.Position := 0;
  end;
end;

procedure TMAILEDITFORM.actLineExecute(Sender: TObject);
var
  V: IHTMLTxtRangeDisp;
begin
  V := DHTMLEdit.DOM.selection.createRange as IHTMLTxtRangeDisp;
  V.pasteHTML('<HR>');
end;

procedure TMAILEDITFORM.actHyperLinkExecute(Sender: TObject);
begin
  DHTMLEdit.ExecCommand(DECMD_HYPERLINK, OLECMDEXECOPT_DODEFAULT);
end;

procedure TMAILEDITFORM.actHyperLinkUpdate(Sender: TObject);
begin
  SetActionState(Sender as TAction, DECMD_HYPERLINK);
end;

procedure TMAILEDITFORM.actRemoveHyperLinkExecute(Sender: TObject);
begin
  DHTMLEdit.ExecCommand(DECMD_UNLINK, OLECMDEXECOPT_DODEFAULT);
end;

procedure TMAILEDITFORM.actRemoveHyperLinkUpdate(Sender: TObject);
begin
  SetActionState(Sender as TAction, DECMD_UNLINK);
end;

procedure TMAILEDITFORM.actPriorityHighExecute(Sender: TObject);
begin
  if msgMail.Priority < '3' then
    msgMail.Priority := '3'
  else
    msgMail.Priority := '1';
  SetModified;
end;

procedure TMAILEDITFORM.actPriorityHighUpdate(Sender: TObject);
begin
  actPriorityHigh.Checked := msgMail.Priority < '3';
  actPriorityHigh.Enabled := not ReadOnly;
end;

procedure TMAILEDITFORM.actPriorityLowExecute(Sender: TObject);
begin
  if msgMail.Priority > '3' then
    msgMail.Priority := '3'
  else
    msgMail.Priority := '5';

  SetModified;
end;

procedure TMAILEDITFORM.actPriorityLowUpdate(Sender: TObject);
begin
  actPriorityLow.Checked := msgMail.Priority > '3';
  actPriorityLow.Enabled := not ReadOnly;
end;

procedure TMAILEDITFORM.actPrintExecute(Sender: TObject);
var
  V: OleVariant;
begin
  if FormatIsHTMLType then
  begin
    V := True;
    DHTMLEdit.PrintDocument(V);
  end
  else
    TextEdit.Print('');
end;

procedure TMAILEDITFORM.actToolbarNormalVisibleExecute(Sender: TObject);
begin
  tbrTools.Visible := not tbrTools.Visible;
end;

procedure TMAILEDITFORM.actToolbarNormalVisibleUpdate(Sender: TObject);
begin
  actToolbarNormalVisible.Checked := tbrTools.Visible;
end;

procedure TMAILEDITFORM.actToolBarFormatVisibleExecute(Sender: TObject);
begin
  tbrFormat.Visible := not tbrFormat.Visible;
end;

procedure TMAILEDITFORM.actToolBarFormatVisibleUpdate(Sender: TObject);
begin
  actToolBarFormatVisible.Checked := tbrFormat.Visible;
end;

procedure TMAILEDITFORM.actLineUpdate(Sender: TObject);
begin
  if FormatIsHTMLType then
    TAction(Sender).Enabled := not ReadOnly
  else if FormatIsTextType then
    TAction(Sender).Enabled := False;
end;

procedure TMAILEDITFORM.actNewUpdate(Sender: TObject);
begin
  actNew.Enabled := (not ReadOnly) or Assigned(OnNew);
end;

procedure TMAILEDITFORM.actOpenUpdate(Sender: TObject);
begin
  actOpen.Enabled := not ReadOnly;
end;

type
  TMessageItemFrind = class(TMessageItem)
  private
    function GetContent: TMessageContent;
  public
    property Content: TMessageContent read GetContent;
  end;

function TMessageItemFrind.GetContent: TMessageContent;
begin
  Result := inherited Content;
end;

procedure OpenAttachment(Handle: THandle; const Operation: string;
  const AMailGUID: string; AMessageItem: TMessageItem); overload;
var
  F: string;
  lStream, lRealStream: TStream;
  lMailAttachment: TMailAttachment;
begin
  if AMessageItem.FileName = '' then
    F := AMessageItem.ContentName
  else
    F := AMessageItem.FileName;
  lStream := THugeMemoryStream.Create(0);
  lMailAttachment := TMailAttachment.Create;
  try
    AMessageItem.GetContent(lStream);
    // 这里依赖 lStream.Size 判断是否加在过流
    if (AMessageItem.AttachmentIndex > 0) and (lStream.Size = 0) then
    begin
      lMailAttachment.Index := AMessageItem.AttachmentIndex;
      MailSystem.LoadMailAttachment(AMailGUID, lMailAttachment);
      lMailAttachment.Content.Position := 0;
      TMessageItemFrind(AMessageItem).Content.Data.Size := lMailAttachment.Content.Size;
      TMessageItemFrind(AMessageItem).Content.Data.CopyFrom(lMailAttachment.Content, lMailAttachment.Content.Size);
      lRealStream := lMailAttachment.Content;
    end
    else
      lRealStream := lStream;

    OpenAttachment(Handle, Operation, F, lRealStream);
  finally
    lStream.Free;
  end;
end;

procedure OpenAttachment(Handle: THandle; const Operation: string; const ACaption: string; AStream: TStream); overload;
begin
  jsSysUtils.ShellOpenStream(AStream, ACaption, Operation, System.IntPtr.Zero, nil);
end;

procedure TMAILEDITFORM.actSaveAsAttachmentExecute(Sender: TObject);
var
  lSaveDialog: TMailSaveDialog;
  mi: TMessageItem;
  lStream: TStream;
  i:integer;
  tmpFileName,dir:string;
  lMailAttachment: TMailAttachment;
begin

  if  lvwAttachments.SelCount=1 then
  begin
      mi := lvwAttachments.Selected.Data as TMessageItem;
      lSaveDialog := TMailSaveDialog.Create(Self);
      try
        lSaveDialog.Filter := '所有文件(*.*)|*.*';
        lSaveDialog.Options := lSaveDialog.Options + [TOpenOption.ofOverwritePrompt];
        if mi.FileName = '' then
          lSaveDialog.FileName := mi.ContentName
        else
          lSaveDialog.FileName := mi.FileName;
        if lSaveDialog.Execute then
        begin
          lMailAttachment := TMailAttachment.Create;
          lStream := TFileStream.Create(lSaveDialog.FileName, Classes.fmCreate);
          try
            mi.GetContent(lStream);
            if (mi.AttachmentIndex > 0) and (lStream.Size = 0) then
            begin
              lMailAttachment.Index := mi.AttachmentIndex;
              MailSystem.LoadMailAttachment(MailGUID, lMailAttachment);
              lMailAttachment.Content.Position := 0;

              lStream.CopyFrom(lMailAttachment.Content, lMailAttachment.Content.Size);

            end

          finally
            lStream.Free;
          end;
        end;
        BringWindowToTop(Handle);
      finally
        lSaveDialog.Free;
      end;
  end
  else begin
   if Business.Forms.FileCtrl.SelectDirectory('选择目录', '',dir) then
   begin
     // lSaveDialog := TMailSaveDialog.Create(Self);

        for i:=0 to lvwAttachments.Items.Count-1 do
        begin
             if   lvwAttachments.Items.Selected then
             begin
                mi := lvwAttachments.Items.Data as TMessageItem;
                if mi.FileName = '' then
                   tmpFileName:=mi.ContentName
                else
                    tmpFileName:=mi.FileName;
                tmpFileName:=dir+'\'+tmpFileName;
                if sysutils.FileExists(tmpFileName) then
                begin
                   if Forms.Application.MessageBox(tmpFileName+#13'文件已经存在是否覆盖?','提示',Borland.Delphi.Windows.MB_ICONWARNING+Borland.Delphi.Windows.MB_OkCancel)=Borland.Delphi.Windows.idok then
                      sysutils.DeleteFile(tmpFileName)
                   else
                     continue;
                end;
                lMailAttachment := TMailAttachment.Create;
                lStream := TFileStream.Create(tmpFileName, Classes.fmCreate);
                try
                  mi.GetContent(lStream);
                  if (mi.AttachmentIndex > 0) and (lStream.Size = 0) then
                  begin
                    lMailAttachment.Index := mi.AttachmentIndex;
                    MailSystem.LoadMailAttachment(MailGUID, lMailAttachment);
                    lMailAttachment.Content.Position := 0;

                    lStream.CopyFrom(lMailAttachment.Content, lMailAttachment.Content.Size);

                  end

                finally
                  lStream.Free;
                end;
             end;
         end;

         BringWindowToTop(Handle);
     end;
  end;
end;

{procedure TMAILEDITFORM.actSaveAsAttachmentExecute(Sender: TObject);
var
  lSaveDialog: TMailSaveDialog;
  mi: TMessageItem;
  lStream: TStream;
  i:integer;
  tmpFileName,dir:string;
  lMailAttachment: TMailAttachment;
begin

  if  lvwAttachments.SelCount=1 then
  begin
      mi := lvwAttachments.Selected.Data as TMessageItem;
      lSaveDialog := TMailSaveDialog.Create(Self);
      try
        lSaveDialog.Filter := '所有文件(*.*)|*.*';
        lSaveDialog.Options := lSaveDialog.Options + [TOpenOption.ofOverwritePrompt];
        if mi.FileName = '' then
          lSaveDialog.FileName := mi.ContentName
        else
          lSaveDialog.FileName := mi.FileName;
        if lSaveDialog.Execute then
        begin
          lMailAttachment := TMailAttachment.Create;
          lStream := TFileStream.Create(lSaveDialog.FileName, Classes.fmCreate);
          try
            mi.GetContent(lStream);
            if (mi.AttachmentIndex > 0) and (lStream.Size = 0) then
            begin
              lMailAttachment.Index := mi.AttachmentIndex;
              MailSystem.LoadMailAttachment(MailGUID, lMailAttachment);
              lMailAttachment.Content.Position := 0;

              lStream.CopyFrom(lMailAttachment.Content, lMailAttachment.Content.Size);

            end

          finally
            lStream.Free;
          end;
        end;
        BringWindowToTop(Handle);
      finally
        lSaveDialog.Free;
      end;
  end
  else begin
   if Business.Forms.FileCtrl.SelectDirectory('选择目录', '',dir) then
   begin
     // lSaveDialog := TMailSaveDialog.Create(Self);

        for i:=0 to lvwAttachments.Items.Count-1 do
        begin
             if   lvwAttachments.Items.Selected then
             begin
                mi := lvwAttachments.Items.Data as TMessageItem;
                if mi.FileName = '' then
                   tmpFileName:=mi.ContentName
                else
                    tmpFileName:=mi.FileName;
                tmpFileName:=dir+'/'+tmpFileName;
                if sysutils.FileExists(tmpFileName) then
                begin
                   if Forms.Application.MessageBox(tmpFileName+#13'文件已经存在是否覆盖?','提示',Borland.Delphi.Windows.MB_ICONWARNING+Borland.Delphi.Windows.MB_OkCancel)=Borland.Delphi.Windows.idok then
                   begin
                         sysutils.DeleteFile(tmpFileName);
                         lMailAttachment := TMailAttachment.Create;
                         lStream := TFileStream.Create(tmpFileName, Classes.fmCreate);
                         try
                         mi.GetContent(lStream);
                         if (mi.AttachmentIndex > 0) and (lStream.Size = 0) then
                             begin
                             lMailAttachment.Index := mi.AttachmentIndex;
                             MailSystem.LoadMailAttachment(MailGUID, lMailAttachment);
                             lMailAttachment.Content.Position := 0;
                             lStream.CopyFrom(lMailAttachment.Content, lMailAttachment.Content.Size);
                             end
                         finally
                         lStream.Free;
                         end;
                   end;
                end;
             end;
         end;

         BringWindowToTop(Handle);
     end;
  end;
end;}



procedure TMAILEDITFORM.actSaveAsAttachmentUpdate(Sender: TObject);
begin
  actSaveAsAttachment.Enabled := Assigned(lvwAttachments.Selected);
end;

procedure TMAILEDITFORM.actOpenAttachmentExecute(Sender: TObject);
begin
  OpenAttachment(Handle, 'Open', MailGUID, lvwAttachments.Selected.Data as TMessageItem);
end;

procedure TMAILEDITFORM.actOpenAttachmentUpdate(Sender: TObject);
begin
  actOpenAttachment.Enabled := Assigned(lvwAttachments.Selected);
end;

procedure TMAILEDITFORM.actPrintAttachmentExecute(Sender: TObject);
begin
  OpenAttachment(Handle, 'Print', MailGUID, lvwAttachments.Selected.Data as TMessageItem);
end;

procedure TMAILEDITFORM.actPrintAttachmentUpdate(Sender: TObject);
begin
  actPrintAttachment.Enabled := Assigned(lvwAttachments.Selected);
end;

procedure TMAILEDITFORM.lvwAttachmentsDblClick(Sender: TObject);
begin
  actOpenAttachment.Execute;
end;

procedure TMAILEDITFORM.actAddressBookExecute(Sender: TObject);
var
  SendTo, CC, BCC: string;
  Changed: Boolean;
begin
  if Assigned(OnAddressBook) then
  begin
    SendTo := edtSendTo.Text;
    CC := edtCC.Text;
    BCC := edtBCC.Text;
    Changed := False;
    OnAddressBook(Self, SendTo, CC, BCC, Changed);
    if Changed then
    begin
      edtSendTo.Text := SendTo;
      edtCC.Text := CC;
      edtBCC.Text := BCC;
      SetModified;
    end;
  end;
end;

procedure TMAILEDITFORM.btnSendToClick(Sender: TObject);
begin
  CheckName;
  actAddressBook.Execute;
end;

function TMAILEDITFORM.CanSend: Boolean;
const
  cTiTle = '邮件';
  cCaption1 = '没有收件人地址!';
  cCaption2 = '请先选择一个发送邮件的帐号!';
begin
  if edtSendTo.Text = '' then
  begin
    MessageBox(Handle, cCaption1, cTitle, MB_IconInformation or MB_Ok);
    Result := False;
  end
  else if (DoGetMailFrom = '') and ((Pos('@', edtSendTo.Text) <> 0) or
    (Pos('@', edtCC.Text) <> 0) or (Pos('@', edtBCC.Text) <> 0)) then
  begin
    MessageBox(Handle, cCaption2, cTitle, MB_IconInformation or MB_Ok);
    Result := False;
  end
  else
    Result := True;
end;

procedure TMAILEDITFORM.CheckName;
begin
  edtSendTo.Text := TMailLibrary.GetMailUsers(edtSendTo.Text);
  edtCC.Text := TMailLibrary.GetMailUsers(edtCC.Text);
  edtBcc.Text := TMailLibrary.GetMailUsers(edtBcc.Text);
end;

{procedure TMAILEDITFORM.LoadAttachments;
var
  I: Integer;
  lAttachments: TStringList;
  lMessageItem: TMessageItem;
  lMailAttachment: TMailAttachment;
begin
  lAttachments := TStringList.Create;
  lMailAttachment := TMailAttachment.Create;
  try
    msgMail.GetAttachFileNames(lAttachments);
    for I := 0 to lAttachments.count - 1 do
    begin
      lMessageItem := lAttachments.Objects[I] as TMessageItem;
      if (lMessageItem.AttachmentIndex > 0) and (TMessageItemFrind(lMessageItem).Content.Data.Size = 0) then
      begin
        lMailAttachment.Index := lMessageItem.AttachmentIndex;
        MailSystem.LoadMailAttachment(MailGUID, lMailAttachment);
        lMailAttachment.Content.Position := 0;
        TMessageItemFrind(lMessageItem).Content.Data.Size := lMailAttachment.Content.Size;
        TMessageItemFrind(lMessageItem).Content.Data.CopyFrom(lMailAttachment.Content, lMailAttachment.Content.Size);
      end;
    end;
  finally
    lMailAttachment.Free;
    lAttachments.Free;
  end;
end;}

procedure TMailEditForm.LoadAttachments;
var
  I: Integer;
  lAttachments: TStringList;
  lMessageItem: TMessageItem;
  lMailAttachment: TMailAttachment;
begin
  lAttachments := TStringList.Create;
  lMailAttachment := TMailAttachment.Create;
  try
    msgMail.GetAttachFileNames(lAttachments);
    for I := 0 to lAttachments.count - 1 do
    begin
      lMessageItem := lAttachments.Objects[I] as TMessageItem;
      if (lMessageItem.AttachmentIndex > 0) and (TMessageItemFrind(lMessageItem).Content.Data.Size = 0) then
      begin
        lMailAttachment.Index := lMessageItem.AttachmentIndex;
        if IsTransmitMail then
          MailSystem.LoadMailAttachment(TransmitMailGUID, lMailAttachment)
        else
          MailSystem.LoadMailAttachment(MailGUID, lMailAttachment);
        lMailAttachment.Content.Position := 0;
        TMessageItemFrind(lMessageItem).Content.Data.Size := lMailAttachment.Content.Size;
        TMessageItemFrind(lMessageItem).Content.Data.CopyFrom(lMailAttachment.Content, lMailAttachment.Content.Size);
      end;
    end;
  finally
    lMailAttachment.Free;
    lAttachments.Free;
  end;
end;


procedure TMAILEDITFORM.actSendExecute(Sender: TObject);
var
  CanSending: Boolean;
  lMailSendReceiveForm: TMailSendReceiveForm;
begin
  lMailSendReceiveForm := TMailSendReceiveForm.Create(nil);
  try
    if not CanSend then Exit;
    Self.Visible := False;
    CanSending := True;
    if Assigned(OnSending) then
      OnSending(Self, CanSending);

    if CanSending then
    begin
      self.Hide;
      self.Repaint;
      Forms.Application.ProcessMessages;
      lMailSendReceiveForm.Show;
      lMailSendReceiveForm.BeforeSend;
      lMailSendReceiveForm.Repaint;
      Forms.Screen.Cursor := Business.Forms.Controls.crHourGlass;
      DoSave(True);
      if Assigned(OnSent) then
        OnSent(Self);
      lMailSendReceiveForm.AfterSend;
      Close;
      Forms.Screen.Cursor := Business.Forms.Controls.crDefault;
    end;
  finally
    lMailSendReceiveForm.Free;
  end;
end;

procedure TMAILEDITFORM.actSendUpdate(Sender: TObject);
begin
  actSend.Enabled := not ReadOnly;
end;

procedure TMAILEDITFORM.actAddressBookUpdate(Sender: TObject);
begin
  actAddressBook.Enabled := not ReadOnly;
end;

procedure TMAILEDITFORM.FormActivate(Sender: TObject);
begin
  SetZOrder(True);
end;

procedure TMAILEDITFORM.cbrToolsBandMove(Sender: TObject;
  Control: TControl; var ARect: TRect);
begin
  if Control = tbMenus then
  begin
    ARect.Left := 0;
    ARect.Right := cbrTools.ClientWidth;
  end;
end;

function TMAILEDITFORM.ColorToHtml(Color: TColor): string;
begin
  result:='#' + IntToHex(Color and $FF,2)+
          IntToHex(Color shr 8 and $FF,2)+
          IntToHex(Color shr 16 and $FF,2);
end;

procedure TMAILEDITFORM.ColorsChange(Sender: TObject);
var
  V: OleVariant;
begin
  if PopupColors.Active then
  begin
    TColorToolButton(PopupColors.Caller).Color := Colors.ActiveColor;
    V :=  ColorToHTML(Colors.ActiveColor);
    if (TColorToolButton(PopupColors.Caller).Tag = 1) then { 1 表示True }
    begin
      if DHTMLEdit.QueryStatus(DECMD_SETFORECOLOR) >= DECMDF_ENABLED then
        DHTMLEdit.ExecCommand(DECMD_SETFORECOLOR, OLECMDEXECOPT_DODEFAULT, V);
    end
    else begin
      if DHTMLEdit.QueryStatus(DECMD_SETBACKCOLOR) >= DECMDF_ENABLED then
        DHTMLEdit.ExecCommand(DECMD_SETBACKCOLOR, OLECMDEXECOPT_DODEFAULT, V);
    end;
  end;
end;

procedure TMAILEDITFORM.ColorsClick(Sender: TObject);
begin
  PopupColors.Close;
end;

procedure TMAILEDITFORM.lvwAttachmentsKeyDown(Sender: TObject;
  var Key: Word; Shift: TShiftState);
begin
  case Key of
    VK_SPACE, VK_RETURN: actOpenAttachment.Execute;
    VK_DELETE: actDeleteAttachment.Execute;
  end;
end;

procedure TMAILEDITFORM.actChangeFormatExecute(Sender: TObject);
const
  csMsg1 = '警告';
  csMsg2 = '改变该邮件的格式,邮件内容将丢失! 是否这样做?';
begin
  if TAction(Sender).Checked then Exit;

  if MessageBox(Handle, PChar(csMsg2), PChar(csMsg1), MB_ICONWARNING or MB_YESNO)
    <> ID_YES then Exit;

  case TAction(Sender).Tag of
    100: msgMail.ContentFormatType := TContentFormatType.ftText;
    101: msgMail.ContentFormatType := TContentFormatType.ftHTML;
  end;
  RefreshEdit;
end;

procedure TMAILEDITFORM.FormShow(Sender: TObject);
begin
  if msgMail.ContentType = '' then
   //设置默认为html格式
       msgMail.ContentFormatType:=  TContentFormatType.ftHTML ;
  //  msgMail.DefaultContentType;

  RefreshEdit;
  RefreshMessage;
  if not ReadOnly then
  begin
    DoRefreshAccountNames;
    DoRefreshMenuByAccountNames;
  end;
end;

procedure TMAILEDITFORM.actTextUpdate(Sender: TObject);
begin
  TAction(Sender).Checked := FormatIsTextType;
  TAction(Sender).Enabled := not ReadOnly;
end;

procedure TMAILEDITFORM.actHTMLUpdate(Sender: TObject);
begin
  TAction(Sender).Checked := FormatIsHTMLType;
  TAction(Sender).Enabled := not ReadOnly;
end;

procedure TMAILEDITFORM.actBlockFormatExecute(Sender: TObject);
var
  I: Integer;
  S: string;
begin
  S := GetBlockFormatName;
  for I := 0 to miBlockFormatNames.Count - 1 do
    miBlockFormatNames[I].Checked := miBlockFormatNames[I].Caption = S;
end;

procedure TMAILEDITFORM.actBlockFormatUpdate(Sender: TObject);
begin
  if FormatIsHTMLType then
    TAction(Sender).Enabled := True
  else if FormatIsTextType then
    TAction(Sender).Enabled := False;
end;

procedure TMAILEDITFORM.cbrToolsResize(Sender: TObject);
begin
  tbrTools.Width := cbrTools.Width;
  tbrFormat.Width := cbrTools.Width;
end;

procedure TMAILEDITFORM.actAboutMailExecute(Sender: TObject);
begin
  if Assigned(OnAbout) then
    OnAbout(Self);
end;

procedure TMAILEDITFORM.pmAccountNamesPopup(Sender: TObject);
begin
  DoRefreshMenuByAccountNames;
end;

procedure TMAILEDITFORM.actAccountsExecute(Sender: TObject);
begin
// todo
end;

procedure TMAILEDITFORM.actAccountsUpdate(Sender: TObject);
begin
  actAccounts.Enabled := (not ReadOnly) and (pmAccountNames.Items.Count > 0);
end;

procedure TMAILEDITFORM.actLaterSendExecute(Sender: TObject);
begin
  if not CanSend then Exit;
  DoSave(True);
  Close;
end;

procedure TMAILEDITFORM.actLaterSendUpdate(Sender: TObject);
begin
  actLaterSend.Enabled := actSend.Enabled;
end;

procedure TMAILEDITFORM.actTransmitExecute(Sender: TObject);
begin
  FMailEditor.TransmitMail(MailGUID, MailPath);
  Close;
end;

procedure TMAILEDITFORM.actTransmitUpdate(Sender: TObject);
begin
  TAction(Sender).Enabled := ReadOnly;
end;

procedure TMAILEDITFORM.actReplyExecute(Sender: TObject);
begin
  FMailEditor.ReplyMail(MailGUID, MailPath);
  Close;
end;

procedure TMAILEDITFORM.actCheckNameExecute(Sender: TObject);
begin
  CheckName;
end;

procedure TMAILEDITFORM.actCheckNameUpdate(Sender: TObject);
begin
  TAction(Sender).Enabled := not ReadOnly;
end;

end.
回复 支持 反对

使用道具 举报

 楼主| 发表于 2008-2-29 13:54:27 | 显示全部楼层
邮件库(上)
unit MAILLIBRARY;

interface

uses
  Business.System, Business.Model, Business.Forms, Business.Data, Business.Model.Org,
  Business.System.SysUtils, Business.System.jsCommon, Business.System.StrUtils,
  Business.System.jsSysUtils, Business.System.FileSys, Business.System.StringUtils,
  Business.Model.ExtUtils, Business.Model.DBMSUtils, Business.Data.BizSchemaTypes,
  Justep.Tools.Mail, SystemCore;

type

{ TMailLibrary }

  // TODO: 邮件结构需要整理,目前没有采用平台典型的开发模式(信息+功能)

  TMAILLIBRARY = class(TBizLibrary)
  private
    static function AddressData: TDataSet;
    static function PersonData: TDataSet;
    static procedure GetEmailByDisplayName(const ADisplayName: string; AIDs: TStrings);
    static procedure GetIDByDisplayName(const ADisplayName: string; AIDs: TStrings);
  public
    static procedure SetOwnerID(const Value: string);
    static function GetOwnerID: string;
    { 根据用户字符串获取存储的用户列表
      用户字符串AUsers的通过“,”间隔多个用户,用户允许的格式为:
        a. "显示名" <mailjustep.com>   // 外部邮件
        b. mailjustep.com              // 外部邮件
        c. "显示名" <UserID>           // 内部邮件
        d. UserID                      // 内部邮件
        e 显示名                       // 通讯簿对应人员有Email时为外部邮件,否则内部邮件
      存储格式为:"显示名1" <UserID2>, "显示名2" <mail@justep.com>
      前面引号部分为显示名,后面尖括号为用户ID或者Email地址  }
    static function GetMailUsers(const AUsers: string): string;
    static function CheckID(const ADisplayName: string; AIDs: TStrings): string;
  end;

  TMailEditorAboutEvent = procedure(Sender: TObject; Editor: TWinControl) of object;
  TMailEditorAddressBookEvent = procedure(Sender: TObject; Editor: TWinControl;
    var SendTo, CC, BCC: string; var Changed: Boolean) of object;
  TMailEditorGetAccountNamesEvent = procedure(Sender: TObject; AccountNames: TStrings) of object;
  TMailEditorGetOperUserInfoEvent = procedure (Sender: TObject;
    out AOperUserID, AOperUserDisplayName: string) of object;
  TMailEditorDeleteEvent = procedure(Sender: TObject; const MailID: string;
    var Deleted: Boolean) of object;
  TMailEditorLoadEvent = procedure(Sender: TObject; const MailID: string;
    AStream: TStream; var AFull: Boolean) of object;
  TMailEditorNewEvent = procedure(Sender: TObject; var MailID: string;
    AStream: TStream) of object;
  TMailEditorReplyEvent = procedure(Sender: TObject; var NewMailID: string;
    const OldMailID: string; AStream: TStream) of object;
  TMailEditorTransmitEvent = procedure(Sender: TObject; var NewMailID: string;
    const OldMailID: string; AStream: TStream) of object;
  TMailEditorSaveEvent = procedure(Sender: TObject; var MailID, MailPath: string;
    AStream: TStream; const APrepareSend: Boolean; var Saved: Boolean) of object;
  TMailEditorSendingEvent = procedure(Sender: TObject; const MailID: string;
    var CanSend: Boolean) of object;
  TMailEditorNotifyEvent = procedure(Sender: TObject; const MailID: string) of object;
  TBeforeMailEditorReplayEvent = procedure(Sender: TObject; AMsg: TMessage) of Object;

  { TCustomMailEditor }

  TCustomMailEditor = class(TComponent)
  private
    FMails: TStrings;
    FOpenedMails: TStrings;

    FOnAddressBook: TMailEditorAddressBookEvent;
    FOnGetAccountNames: TMailEditorGetAccountNamesEvent;
    FOnGetOperUserInfo: TMailEditorGetOperUserInfoEvent;
    FOnAbout: TMailEditorAboutEvent;
    FOnDelete: TMailEditorDeleteEvent;
    FOnLoad: TMailEditorLoadEvent;
    FOnNew: TMailEditorNewEvent;
    FOnReply: TMailEditorReplyEvent;
    FOnTransmit: TMailEditorTransmitEvent;
    FOnSave: TMailEditorSaveEvent;
    FOnSending: TMailEditorSendingEvent;
    FOnSent: TMailEditorNotifyEvent;
    FBeforeReply: TBeforeMailEditorReplayEvent;


    function GetMailInfo(const AMailID: string): TMailViewInfo;
    function MailStream(const AMailID: string): TStream;
//    function GetMailPath(const AMailID: string): string;//TODO: to delete
    procedure AddressBook(Sender: TObject; var SendTo, CC, BCC: string;
      var Changed: Boolean);
    procedure GetAccountNames(Sender: TObject; AccountNames: TStrings);
    procedure GetOperUserInfo(Sender: TObject; out AOperUserID, AOperUserDisplayName: string);
    procedure About(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure Save(Sender: TObject; AStream: TStream;
      const APrepareSend: Boolean; var Saved: Boolean);
    procedure Sending(Sender: TObject; var CanSend: Boolean);
    procedure Sent(Sender: TObject);
    //SMP Modify 20080824用来传入保存邮件回复时的头
    //procedure DoEdit(const AMailID, AMailPath: string; const AReadOnly: Boolean; AReplayHeader: String = '');
    procedure DoEdit(const AMailID, AMailPath: string; const AReadOnly: Boolean; AReplayHeader: String = '');   overload;
    procedure DoEdit(const AMailID, AMailPath: string; const AReadOnly: Boolean; AReplayHeader: String = ''; ATransmitMailGUID: String);   overload;
    procedure DoReplay(AMsg: TMessage);
    procedure OnNewMail(Sender: TObject);
  protected
    property OnAddressBook: TMailEditorAddressBookEvent read FOnAddressBook write FOnAddressBook;
    property OnGetAccountNames: TMailEditorGetAccountNamesEvent
      read FOnGetAccountNames write FOnGetAccountNames;
    property OnGetOperUserInfo: TMailEditorGetOperUserInfoEvent
      read FOnGetOperUserInfo write FOnGetOperUserInfo;
    property OnAbout: TMailEditorAboutEvent read FOnAbout write FOnAbout;
    property OnDelete: TMailEditorDeleteEvent read FOnDelete write FOnDelete;
    property OnLoad: TMailEditorLoadEvent read FOnLoad write FOnLoad;
    property OnNew: TMailEditorNewEvent read FOnNew write FOnNew;
    property OnReply: TMailEditorReplyEvent read FOnReply write FOnReply;
    property OnTransmit: TMailEditorTransmitEvent read FOnTransmit write FOnTransmit;
    property OnSave: TMailEditorSaveEvent read FOnSave write FOnSave;
    property OnSending: TMailEditorSendingEvent read FOnSending write FOnSending;
    property OnSent: TMailEditorNotifyEvent read FOnSent write FOnSent;
    property BeforeReply: TBeforeMailEditorReplayEvent read FBeforeReply write FBeforeReply;
  public
    constructor Create(AOwner: TComponent);
    destructor Destroy; override;

    function FullLoaded(const AMailID: string): Boolean;
    function HasHeader(const AMailID: string): Boolean;
    function NewMail: string;
    function ReplyMail(const AMailID, AMailPath: string): string;
    function TransmitMail(const AMailID, AMailPath: string): string;
    procedure AddMail(const AMailID: string; AStream: TStream;
      AFull, AReadOnly: Boolean);
    procedure Clear;
    procedure Delete(const AMailID: string);
    procedure Edit(const AMailID, AMailPath: string);
    procedure View(const AMailID, AMailPath: string);
    procedure GetMail(const AMailID: string; AStream: TStream);
    procedure GetMailHeader(const AMailID: string; AStream: TStream);
    procedure LoadMail(const AMailID: string; AFull: Boolean);

    property Mails: TStrings read FMails;
  end;

  { TMailEditor }

  TMailEditor = class(TCustomMailEditor)
  public
{ TODO:    property OnAddressBook;
    property OnGetAccountNames;
    property OnGetOperUserInfo;
    property OnAbout;
    property OnDelete;
    property OnLoad;
    property OnNew;
    property OnSave;
    property OnSending;
    property OnSent;
    property BeforeReply;}
  end;

  { TMailViewer }

  TMailViewer = class(TWinControl)
  private
    FContext: TContext;
    FViewer: TWinControl;
    function GetLoadAttachContent: TLoadAttachContentEvent;
    procedure SetLoadAttachContent(const Value: TLoadAttachContentEvent);
    function GetWebBrowser: TWebBrowser;
  public
    constructor Create(AContext: TContext);

    procedure Clear;
    procedure LoadFromFile(const AFileName: string);
    procedure LoadFromStream(AStream: TStream);
    procedure ViewMail(const AMailGUID: string; AContent: TStream; AAttachmentInfos: TList);

    property WebBrowser: TWebBrowser read GetWebBrowser;

{ TODO:    property Align;
    property Anchors;
    property BevelEdges;
    property BevelInner;
    property BevelOuter;
    property BevelKind;
    property BevelWidth;
    property BiDiMode;
    property BorderWidth;
    property Color;
    property Constraints;
    property UseDockManager;
    property DragKind;
    property DragMode;
    property Enabled;
    property ParentFont;
    property Font;
    property Height;
    property ParentBiDiMode;
    property PopupMenu;
    property ShowHint;
    property Visible;
    property Width;
    property OnCanResize;
    property OnClick;
    property OnConstrainedResize;
    property OnContextPopup;
    property OnDblClick;
    property OnDragDrop;
    property OnDragOver;
    property OnKeyDown;
    property OnKeyPress;
    property OnKeyUp;
    property OnMouseDown;
    property OnMouseMove;
    property OnMouseUp;
    property OnMouseWheel;
    property OnMouseWheelDown;
    property OnMouseWheelUp;     }
    property OnLoadAttachContent: TLoadAttachContentEvent read GetLoadAttachContent
      write SetLoadAttachContent;
  end;

const
  cGUID_Root    = '-1';
  cGUID_InBox   = 'BIZ_INBOX';
  cGUID_OutBox  = 'BIZ_OUTBOX';
  cGUID_Draft   = 'BIZ_DRAFT';
  cGUID_SentBox = 'BIZ_SENTBOX';
  cGUID_DelBox  = 'BIZ_DELBOX';

  cRootText   = '邮件文件夹';
  cInboxText  = '收件箱';
  cOutBoxText = '发件箱';
  cDraftText  = '草稿';
  cSentBoxText   = '已发送邮件';
  cDelBoxText = '已删除邮件';

  msUnknown    = 0;
  msReceived   = 1;
  msCreated    = 2;
  msSent       = 4;
  msReaded     = 8;
  msReplied    = 16;
  msSending    = 32;
  msTranSmited = 64;
  msInternal   = 128;                             // 内部邮件
  msLoaded     = 256;                             // 没有Load的内部邮件

  csMailOption     = 'MailOption';
  csMailColumns    = 'MailColumns';
  csMailSettingFile = 'JustMail.ini';
  csFolderWidth    = 'FolderWidth';
  csMailListHeight = 'MailListHeight';

  cConnectionStr = 'DATABASEURL=Biz:\COLLABORATION\Collaboration.Database';

type

{ TMailAttachment }

  TMailAttachment = class(TPersistent)
  private
    FMailGUID: string;
    FContent: TStream;
    FIndex: Integer;
    procedure SetMailGUID(const Value: string);
    function GetContent: TStream;
    procedure SetContent(const Value: TStream);
    function GetHashValue: string;
    function GetSize: Integer;
  public
    constructor Create;
    destructor  Destroy; override;
    procedure Assign(Source: TPersistent); override;

    property MailGUID: string read FMailGUID write SetMailGUID;
    property Index: Integer read FIndex write FIndex;
    property Size: Integer read GetSize;
    property HashValue: string read GetHashValue;
    property Content: TStream read GetContent write SetContent;
  end;

{ TMailInfo }

  TMailInfo = class(TPersistent)
  private
    FMailGUID: string;
    FUIDL: string;
    FSize: Integer;
    FHasAttach: Boolean;
    FTos: string;
    FFrom: string;
    FCc: string;
    FBcc: string;
    FReplyTo: string;
    FSubject: string;
    FPriority: Integer;
    FDate: TDateTime;
    FAttachmentInfos: TStrings;
    FHeaderText: string;
    function GetHeaderText: string;
    procedure SetHeaderText(const Value: string);
    procedure UpdateProperties;
  public
    constructor Create;
    destructor Destroy; override;
    procedure Assign(Source: TPersistent); override;

    property MailGUID: string read FMailGUID write FMailGUID;
    property UIDL: string read FUIDL write FUIDL;
    property Size: Integer read FSize write FSize;
    property HasAttach: Boolean read FHasAttach write FHasAttach;

    property Tos: string read FTos write FTos;
    property From: string read FFrom write FFrom;
    property Cc: string read FCc write FCc;
    property Bcc: string read FBcc write FBcc;
    property ReplyTo: string read FReplyTo write FReplyTo;
    property Subject: string read FSubject write FSubject;
    property Priority: Integer read FPriority write FPriority;
    property Date: TDateTime read FDate write FDate;
    property AttachmentInfos: TStrings read FAttachmentInfos;//格式: 邮件大小*文件名
    property HeaderText: string read GetHeaderText write SetHeaderText;
  end;

{ TMailReference }

  TMailReference = class(TPersistent)
  private
    FMailGUID: string;
    FOwnerGUID: string;
    FAccountGUID: string;
    FPathGUID: string;
    FTime: TDateTime;
    FSize: Integer;
    FState: Integer;

    FMailInfo: TMailInfo;
    procedure SetMailInfo(const Value: TMailInfo);
  public
    constructor Create;
    destructor Destroy; override;
    procedure Assign(Source: TPersistent); override;

    property MailGUID: string read FMailGUID write FMailGUID;
    property OwnerGUID: string read FOwnerGUID write FOwnerGUID;
    property AccountGUID: string read FAccountGUID write FAccountGUID;
    property PathGUID: string read FPathGUID write FPathGUID;
    property Time: TDateTime read FTime write FTime;
    property Size: Integer read FSize write FSize;
    property State: Integer read FState write FState;

    property MailInfo: TMailInfo read FMailInfo write SetMailInfo;
  end;

{ TMailPath }

  TMailPath = class(TPersistent)
  private
    FParentPath: string;
    FPathGUID: string;
    FDisplayName: string;
    FOwnerGUID: string;
    FParentGUID: string;
    FReadOnly: Boolean;
    function GetFullPath: string;
  public
    constructor Create;
    procedure Assign(Source: TPersistent); override;

    property PathGUID: string read FPathGUID write FPathGUID;
    property ParentGUID: string read FParentGUID write FParentGUID;
    property OwnerGUID: string read FOwnerGUID write FOwnerGUID;
    property DisplayName: string read FDisplayName write FDisplayName;
    property ParentPath: string read FParentPath write FParentPath;
    property ReadOnly: Boolean read FReadOnly;
    property FullPath: string read GetFullPath;

  end;

{ TMailAccount }

  TMailAccount = class(TPersistent)
  private
    FAccountGUID: string;
    FAccountID: string;
    FMailAddress: string;
    FOwnerID: string;

    FPOPHost: string;
    FPOPPort: Integer;
    FSMTPHost: string;
    FSMTPPort: Integer;

    FUserID: string;
    FPassword: string;

    FIsDefault: Boolean;
    FDeleteOnRetrieve: Boolean;

    FDifferPOP: Boolean;
    FVerify: Boolean;
    FVerifyAccount: string;
    FVerifyPassword: string;
  public
    constructor Create;
    destructor  Destroy; override;

    procedure Assign(Source: TPersistent); override;
    procedure LoadPropertiesFromStream(AStream: TStream);
    procedure SavePropertiesToStream(AStream: TStream);

    //下面的属性是需要保存的
    property AccountGUID: string read FAccountGUID write FAccountGUID;
    property AccountID: string read FAccountID write FAccountID;
    property OwnerID: string read FOwnerID write FOwnerID;
    property MailAddress: string read FMailAddress write FMailAddress;

    property POPHost: string read FPOPHost write FPOPHost;
    property POPPort: Integer read FPOPPort write FPOPPort;
    property SMTPHost: string read FSMTPHost write FSMTPHost;
    property SMTPPort: Integer read FSMTPPort write FSMTPPort;

    property UserID: string read FUserID write FUserID;
    property Password: string read FPassword write FPassword;
    property DifferPOP: Boolean read FDifferPOP write FDifferPOP;
    property Verify: Boolean read FVerify write FVerify;
    property VerifyAccount: string read FVerifyAccount write FVerifyAccount;
    property VerifyPassword: string read FVerifyPassword write FVerifyPassword;
    property DeleteOnRetrieve: Boolean read FDeleteOnRetrieve write FDeleteOnRetrieve;

    property IsDefault: Boolean read FIsDefault write FIsDefault;
  end;

{ TMailAccounts }

  TMailAccounts = class(TObjectList)
  protected
    function  GetItems(Index: Integer): TMailAccount;
    procedure SetItems(Index: Integer; AMailAccount: TMailAccount);
  public
    procedure Clear; override;

    function  Add(AMailAccount: TMailAccount): Integer;
    function  Extract(Item: TMailAccount): TMailAccount;
    function  Remove(AMailAccount: TMailAccount): Integer;
    function  IndexOf(AMailAccount: TMailAccount): Integer;
    function  First: TMailAccount;
    function  Last: TMailAccount;
    procedure Insert(Index: Integer; AMailAccount: TMailAccount);
    function  FindAccountByGUID(const AccountGUID: string): TMailAccount;
    function  FindAccountByAddress(const MailAddress: string): TMailAccount;
    function  FindAccountByID(const AccountID: string): TMailAccount;

    property  Items[Index: Integer]: TMailAccount read GetItems write SetItems; default;
  end;

  TSearchMailParam = record
    MatchGUIDs: string;         { 用';'分隔多个GUID }

    { 属性条件 }
    MatchSubject: string;
    MatchFrom: string;
    MatchTo: string;
    MatchUIDL: string;

    BeginDate: TDateTime;
    EndDate: TDateTime;
    LeastSize: Integer;

    SearchSubPath: Boolean;
  end;

  TMailStorageAction = (msaLoad, msaSave, msaDelete, msaMove);

  TMailDataKind = (mskInfo, mskStream, mskReference, mskPath, mskAccount);

  TMailScopeKind = (skPath, skGUID);

  TMailKey = record
    Scope: string;
    ScopeKind: TMailScopeKind;
    OwnerGUID : string;
    PathGUID: string;
  end;

  TSentMailEvent = procedure (Sender: TObject; const AMailGUID: string) of object;
  TReceiveNewMailEvent = procedure (Sender: TObject; const AMailGUID: string) of object;
  TSendInternalMailEvent = procedure (Sender: TObject;
    const AMailCount, AMailIndex: Integer) of object;
  TLoadFolderMailsEvent = procedure (Sender: TObject; AMailList: TStrings;
    const AFolderGUID: string) of object;

{ TMailSystem }

  TMailSystem = class(TObject)
  private
    FMailInfoList: TStringList;
    FMailRefList: TStringList;
    FAccounts: TMailAccounts;

    FReceiveMsgExchg: TMessageExchanger;
    FSendMsgExchg: TMessageExchanger;
    FOutMails: TStringList;
    FMailSending: TStringList;
    FOnSentMail: TSentMailEvent;
    FOnReceiveNewMail: TReceiveNewMailEvent;
    FOnSendInternalMail: TSendInternalMailEvent;
    FOnBeforeStartMailExchange: TNotifyEvent;
    FLoadFolderMails: TLoadFolderMailsEvent;
    FAfterSend: TNotifyEvent;

    function GetOperUser: string;
    function GetOperUserDisplayName: string;
    function MakeKey(const AScope: string; const AScopeKind: TMailScopeKind;
      const AOwnerGUID, APathGUID: string): TMailKey;
    function GetAccounts(const Index: Integer): TMailAccount;
    procedure CheckAccounts;
    function GetDefaultMailPath(const APathGUID: string; AMailPath: TMailPath): Boolean;
  protected
    function DoSearchMail(const AOperateUser, APath: string;
      const ASearchMailParam: TSearchMailParam; AMailRefList: TStrings): Integer;
    function DoGetAccountList(const AOperateUser: string; AMailAccountList: TStrings): Integer;

    procedure AddMailRef(var AMailRef: TMailReference);
    procedure AddMailInfo(AMailInfo: TMailInfo);
    procedure DeleteMailRef(const AMailGUID, APath: string);
    procedure DeleteMailInfo(const AMailGUID: string);
    function DoFindMailRef(const AMailGUID, APath: string): TMailReference;
    function DoFindMailInfo(const AMailGUID: string): TMailInfo;
    procedure DoSentMail(const AMailGUID: string);
    procedure DoReceiveNewMail(const AMailGUID: string);
  public
    constructor Create;
    destructor Destroy; override;

    static function CreateMailSystem: TMailSystem;
    static procedure DestroyMailSystem;

    { **************************** 邮件接口 *********************************** }
    function FindMailInfo(const AMailGUID: string): TMailInfo;//包含附件列表
    function FindMailRef(const AMailGUID, APath: string): TMailReference;

    function GetMailInfo(const AMailGUID: string): TMailInfo;
    function GetMailRef(const AMailGUID, APath: string): TMailReference;

    { 如果AMailContent和AMailAttachments为nil,就表示忽略它们 }
    { 如果AMailRef的MailGUID存在就删除后添加,但要使用相同的MailGUID }
    procedure SaveMail(AMailRef: TMailReference; AMailContent: TStream;
      AMailAttachments: TList); overload;
    { 创建一个Mail实体和引用,在内部把MailStream解析为Header、Content和Attachment }
    function SaveMail(const AMailGUID, APath: string;
      AMailStream: TStream): TMailReference; overload;
    { 只能够删除邮件引用,如果引用被删除完了,就删除邮件实体 }
    procedure DeleteMail(const AMailGUID, APath: string);
    procedure MoveMail(const AMailGUID: string; const AOldPath, ANewPath: string);

    { 有就删除后添加,没有就添加 }
    procedure SaveMailRef(AMailRef: TMailReference);
    procedure SaveMailRefList(AMailRefList: TObjectList);

    procedure LoadMailContent(const AMailGUID: string; AStream: TStream);
    procedure LoadMailAttachment(const AMailGUID: string;
      AMailAttachment: TMailAttachment); overload;
    procedure LoadMailAttachment(const AMailGUID: string; Index: Integer; AStream: TStream); overload;

    { 如果AMailRefList为nil,表示不需要返回邮件相关的列表 }
    function SearchMail(const APath: string;
      const ASearchMailParam: TSearchMailParam; AMailRefList: TStrings): Integer;
    function SearcbMailUIDL(const AUIDL, AOperateUser: string;
      AMailRefList: TStrings): Boolean;

    function SearchMailCount(const AMailGUID, AOperateUser: string): Integer;

    { **************************** 邮件目录接口 ******************************* }
    procedure GetMailPathList(APathList: TStrings);
    procedure GetMailPath(const APathGUID: string; AMailPath: TMailPath);
    function AddMailPath(const AGUID, AParentPathGUID, APathDisplayname: string): TMailPath;
    //删除目录的时候同时删除该目录下面的子目录和相关的邮件引用
    procedure DeletePath(const APathGUID: string);
    procedure MovePath(const APathGUID, AOldParentPathGUID, ANewParentPathGUID: string);

    { **************************** 邮件帐号接口 ******************************* }
    procedure AddAccount(AMailAccount: TMailAccount);
    procedure SaveAccount(AMailAccount: TMailAccount);
    procedure DeleteAccount(AMailAccount: TMailAccount);
    function AccountCount: Integer;
    function FindAccountByGUID(const AccountGUID: string): TMailAccount;
    function FindAccountByAddress(const MailAddress: string): TMailAccount;
    function FindAccountByID(const AccountID: string): TMailAccount;

    { *************************** 邮件流接口 ********************************** }
    procedure MsgStreamToMail(AMailRef: TMailReference;
      AMailContent: TStream; AMailAttachments: TList; AMsgStream: TStream);
    procedure MailToMsgStream(AMailInfo: TMailInfo; AMailContent: TStream;
      AMailAttachments: TList; AMsgStream: TStream);
    static function UnloadStreamHeader: string;

    { ************************** 发送接收接口 ********************************* }
    procedure Send; overload;
    procedure Send(MailList: TStrings); overload;
    procedure Send(const AMailGUID: string; const AMailCount, AMailIndex: Integer); overload;

    procedure StartReceiveMail(AccountGUIDs: TStrings);
    procedure SendExternalMail(const AMailGUID, ASenders: string);
    procedure BeforeReceivMail(const AccountGUID, UIDL: String; var Receive: Boolean);
    procedure ReceiveMail(const AccountGUID: String; AStream: TStream;
      const UIDL: String; var Delete: Boolean);
    procedure SentMail(MailIndex: Integer);

    procedure ReceiveInternalMail;
    procedure BeforeStartMailExchange;
    procedure SendInternalMail(const AMailGUID, ASenders: string;
      const AMailCount, AMailIndex: Integer);

    procedure StartMailExchange(const AReceive, ASend: Boolean);

    property ReceiveMsgExchg: TMessageExchanger read FReceiveMsgExchg;
    property SendMsgExchg: TMessageExchanger read FSendMsgExchg;

    property OnSentMail: TSentMailEvent read FOnSentMail write FOnSentMail;
    property OnReceiveNewMail: TReceiveNewMailEvent read FOnReceiveNewMail
      write FOnReceiveNewMail;
    property OnSendInternalMail: TSendInternalMailEvent read FOnSendInternalMail
      write FOnSendInternalMail;
    property OnBeforeStartMailExchange: TNotifyEvent read FOnBeforeStartMailExchange
      write FOnBeforeStartMailExchange;

    property OutMails: TStringList read FOutMails;
    property MailSending: TStringList read FMailSending;
    property LoadFolderMails: TLoadFolderMailsEvent read FLoadFolderMails
      write FLoadFolderMails;

    property AfterSend: TNotifyEvent read FAfterSend write FAfterSend;

    property Accounts[Index: Integer]: TMailAccount read GetAccounts;

    property OperateUser: string read GetOperUser;
    property OperateUserDisplayName: string read GetOperUserDisplayName;
  end;

{ TMailStorageCommand }

  TMailStorageCommand = class(TObject)
  protected
    function GetDataSetIndex: Integer; abstract;

    procedure DoLoad(AStorage: TMailStorage); virtual;
    procedure DoSave(AStorage: TMailStorage); virtual;
  public
    Key: TMailKey;
    Kind: TMailDataKind;

    procedure DoStorage(AStorage: TMailStorage); virtual;
  end;

{ TMailInfoStorageCommand }

  TMailInfoStorageCommand = class(TMailStorageCommand)
  private
    FMailInfo: TMailInfo;
  protected
    function GetDataSetIndex: Integer; override;

    procedure DoLoad(AStorage: TMailStorage); override;
    procedure DoSave(AStorage: TMailStorage); override;
  public
    constructor Create(AMailInfo: TMailInfo);
  end;

{ TMailStreamStorageCommand }

  TMailStreamStorageCommand = class(TMailStorageCommand)
  private
    FMailAttachment: TMailAttachment;
  protected
    function GetDataSetIndex: Integer; override;

    procedure DoLoad(AStorage: TMailStorage); override;
    procedure DoSave(AStorage: TMailStorage); override;

    procedure SetStream(ADataSet: TDataSet; const AIndex: Integer;
      AStream: TStream);
    procedure SetDataSet(ADataSet: TDataSet; const AIndex: Integer;
      AStream: TStream);
  public
    constructor Create(AMailAttachment: TMailAttachment);
  end;

{ TMailMultiStreamStorageCommand }

  TMailMultiStreamStorageCommand = class(TMailStreamStorageCommand)
  private
    FStreamList: TList;
    FStartIndex: Integer;
  protected
    procedure DoLoad(AStorage: TMailStorage); override;
    procedure DoSave(AStorage: TMailStorage); override;
  public
    constructor Create(AStreamList: TList; const AStartIndex: Integer);
  end;

{ TMailRefStorageCommand }

  TMailRefStorageCommand = class(TMailStorageCommand)
  private
    FMailRef: TMailReference;
  protected
    function GetDataSetIndex: Integer; override;

    procedure DoLoad(AStorage: TMailStorage); override;
    procedure DoSave(AStorage: TMailStorage); override;

    procedure SetMailRef(ADataSet: TDataSet; AMailRef: TMailReference);
  public
    constructor Create(AMailRef: TMailReference);
  end;

{ TMailMulitRefStorageCommand }

  TMailMulitRefStorageCommand = class(TMailRefStorageCommand)
  private
    FMailRefList: TStrings;
    FSearchMailParam: TSearchMailParam;
  protected
    procedure DoLoad(AStorage: TMailStorage); override;
    procedure DoSave(AStorage: TMailStorage); override;
  public
    constructor Create(ASearchMailParam: TSearchMailParam; AMailRefList: TStrings);
  end;

{ TMailMoveRefStorageCommand }

  TMailMoveRefStorageCommand = class(TMailStorageCommand)
  private
    FNewPath: string;
  protected
    function GetDataSetIndex: Integer; override;
  public
    constructor Create(const ANewPath: string);
  end;

{ TMailPathStorageCommand }

  TMailPathStorageCommand = class(TMailStorageCommand)
  private
    FMailPath: TMailPath;
  protected
    function GetDataSetIndex: Integer; override;

    procedure DoLoad(AStorage: TMailStorage); override;
    procedure DoSave(AStorage: TMailStorage); override;

    procedure SetPath(ADataSet: TDataSet; AMailPath: TMailPath);
  public
    constructor Create(AMailPath: TMailPath);
  end;

{ TMailMulitPathStorageCommand }

  TMailMulitPathStorageCommand = class(TMailPathStorageCommand)
  private
    FMailPathList: TStrings;
  protected
    procedure DoLoad(AStorage: TMailStorage); override;
    procedure DoSave(AStorage: TMailStorage); override;
  public
    constructor Create(AMailPathList: TStrings);
  end;

{ TMailMovePathStorageCommand }

  TMailMovePathStorageCommand = class(TMailStorageCommand)
  private
    FOldParent: string;
    FNewParent: string;
    FOldPath: string;
    FNewPath: string;
  protected
    function GetDataSetIndex: Integer; override;
  public
    constructor Create(const AOldParent, ANewParent, AOldPath, ANewPath: string);
  end;

{ TMailAccountStorageCommand }

  TMailAccountStorageCommand = class(TMailStorageCommand)
  private
    FMailAccount: TMailAccount;
  protected
    function GetDataSetIndex: Integer; override;

    procedure DoLoad(AStorage: TMailStorage); override;
    procedure DoSave(AStorage: TMailStorage); override;

    procedure SetAccount(ADataSet: TDataSet; AMailAccount: TMailAccount);
  public
    constructor Create(AMailAccount: TMailAccount);
  end;

{ TMailMulitAccountStorageCommand }

  TMailMulitAccountStorageCommand = class(TMailAccountStorageCommand)
  private
    FMailAccountList: TStrings;
  protected
    procedure DoLoad(AStorage: TMailStorage); override;
    procedure DoSave(AStorage: TMailStorage); override;
  public
    constructor Create(AMailAccountList: TStrings);
  end;

{ TMailStorage }

  TMailStorage = class(TSQLDataSetBatch)
  private
    FAction: TMailStorageAction;
    FCommands: TObjectList;

    function GetSQLFromCommandList(ACommands: TObjectList;
      const AMailDataKind: TMailDataKind; const AFieldProfix: string): string;
    function GetSQLBySearchParam(AKey: TMailKey; ASearchMailParam: TSearchMailParam): string;
    function GetDateSQL(const ADateTime: TDateTime; const AFieldName, ASymbol: string): string;
  protected
    function GetNeedSave: Boolean; override;
    //procedure InitDataSets; override;
    //procedure InternalExecute; override;
    //procedure ClearSys; override;
    procedure PrepareSQLAndMaps; override;
    procedure DoExecute; override;
  public
    constructor Create(const AAction: TMailStorageAction);
    destructor Destroy; override;

    procedure AddMailInfoCommand(const AKey: TMailKey; AMailInfo: TMailInfo);

    procedure AddStreamCommand(const AKey: TMailKey; AMailAttachment: TMailAttachment);

    procedure AddMultiAttachmentCommand(const AKey: TMailKey; AStreamList: TList;
      const AStartIndex: Integer);

    procedure AddMailRefCommand(const AKey: TMailKey; AMailRef: TMailReference);

    procedure AddMailMulitRefCommand(const AKey: TMailKey;
      ASearchMailParam: TSearchMailParam; AMailRefList: TStrings);

    procedure AddMoveRefCommand(const AKey: TMailKey; const ANewPath: string);

    procedure AddPathCommand(const AKey: TMailKey; AMailPath: TMailPath);

    procedure AddMulitPathCommand(const AKey: TMailKey; AMailPathList: TStrings);

    procedure AddMovePathCommand(const AKey: TMailKey;
      const AOldParent, ANewParent, AOldPath, ANewPath: string);

    procedure AddAccountCommand(const Akey: TMailKey; AMailAccount: TMailAccount);
    procedure AddMulitAccountCommand(const AKey: TMailKey; AMailAccountList: TStrings);

    property Action: TMailStorageAction read FAction;
  end;

{ TMessage2Mail }

  TMessage2Mail = class(TObject)
  public
    procedure MessageToMail(AMessage: TMessage; AMailInfo: TMailInfo;
      AMailContent: TStream; AMailAttachments: TList);
    procedure MailToMessage(AMailInfo: TMailInfo; AMailContent: TStream;
      AMailAttachments: TList; AMessage: TMessage);
    procedure ToMailAttachments(AMessage: TMessage; var HasAttach: Boolean;
      Attachments: TList; AttachmentInfos: TStrings);
  end;

{ TMailSetting }

  TMailSetting = class(TPersistent)
  protected
    function GetValueName: string; abstract;
    property ValueName: string read GetValueName;
  public
    procedure LoadFromStream(Stream: TStream); abstract;
    procedure LoadFromFile(const FileName: string); virtual;
    procedure SaveToStream(Stream: TStream); abstract;
    procedure SaveToFile(const FileName: string); virtual;
  end;

{ TSendedMailState }

  TSendedMailState = class(TObject)
  private
    FMailGUID: String;
    function getState: String;
    function getHints: String;
  public
    constructor Create(const AMailGUID: String);
    property State: String read getState;
    property Hints: String read getHints;
  end;

function SearchMailParam(const AMatchGUIDs, AMatchSubject, AMatchFrom, AMatchTo,
  AMatchUIDL: string; const ABeginDate, AEndDate: TDateTime;
  const ALeastSize: Integer; const ASearchSubPath: Boolean): TSearchMailParam; overload;
function SearchMailParam(const AMatchGUIDs: string;
  const ASearchSubPath: Boolean): TSearchMailParam; overload;

function GetExecPath: string;
procedure FreeAndNil(var Obj: TObject);

var
  MailSystem: TMailSystem;

const
  cAccountSection = 'MailAccountSection';

implementation

var
  FOwnerID: string;

{ TMailLibrary }

var
  FAddressData: TDataSet;
  FPersonData: TDataSet;

static function TMAILLIBRARY.AddressData: TDataSet;
const
  cSQL = 'SELECT FPERSONID, FNAME, FEMAIL1, FEMAIL2, FEMAIL3 FROM TADDRESSLIST ORDER BY FNAME';
begin
  if FAddressData = nil then
  begin
    FAddressData := TQuery.Create(nil);
    FAddressData.Connection := ExtUtils.SystemConnection;
    TQuery(FAddressData).CommandText := cSQL;
    FAddressData.Open;
  end;
  Result := FAddressData;
end;

static function TMAILLIBRARY.PersonData: TDataSet;
const
  cSQL = 'SELECT FID, FDISPLAYNAME FROM TPERSON ORDER BY FDISPLAYNAME';
begin
  if FPersonData = nil then
  begin
    FPersonData := TQuery.Create(nil);
    FPersonData.Connection := ExtUtils.SystemConnection;
    TQuery(FPersonData).CommandText := cSQL;
    FPersonData.Open;
  end;
  Result := FPersonData;
end;

static procedure TMAILLIBRARY.SetOwnerID(const Value: string);
begin
  FOwnerID := Value;
end;

static function TMAILLIBRARY.GetOwnerID: string;
begin
  Result := FOwnerID;
end;

static procedure TMAILLIBRARY.GetEmailByDisplayName(const ADisplayName: string; AIDs: TStrings);
const
  cKey = 'FNAME';
  cEmail1 = 'FEMAIL1';
  cEmail2 = 'FEMAIL2';
  cEmail3 = 'FEMAIL3';
begin
  // 通讯簿数据集中查找 TODO: 释放
  if AddressData.Locate(cKey, [ADisplayName], []) then
  begin
    while SysUtils.SameText(AddressData.FieldByName(cKey).AsString, ADisplayName) do
    begin
      if AddressData.FieldByName(cEmail1).AsString <> '' then
        AIDs.Add(AddressData.FieldByName(cEmail1).AsString);
      if AddressData.FieldByName(cEmail2).AsString <> '' then
        AIDs.Add(AddressData.FieldByName(cEmail2).AsString);
      if AddressData.FieldByName(cEmail3).AsString <> '' then
        AIDs.Add(AddressData.FieldByName(cEmail3).AsString);

      AddressData.Next;
    end;
  end;
end;

static procedure TMAILLIBRARY.GetIDByDisplayName(const ADisplayName: string; AIDs: TStrings);
const
  cKey = 'FDISPLAYNAME';
  cID = 'FID';
begin
  // 人员表中查找 TODO: 释放
  if PersonData.Locate(cKey, [ADisplayName], []) then
  begin
    while SysUtils.SameText(PersonData.FieldByName(cKey).AsString, ADisplayName) do
    begin
      AIDs.Add(PersonData.FieldByName(cID).AsString);

      PersonData.Next;
    end;
  end;
end;

static function TMAILLIBRARY.GetMailUsers(const AUsers: string): string;
var
  lUsers: string;
  lTempStrings, lIDs: TStringList;
  lPos: Integer;
  lID, lDisplayName: string;
  I: Integer;
begin
  Result := '';
  lTempStrings := TStringList.Create;
  lIDs := TStringList.Create;
  try
    // 兼容分号,全角的分号和逗号
    lUsers := StrUtils.AnsiReplaceStr(AUsers, ';', ',');
    lUsers := StrUtils.AnsiReplaceStr(lUsers, ';', ',');
    lUsers := StrUtils.AnsiReplaceStr(lUsers, ',', ',');

    jsCommon.SplitStrEx(',', lUsers, lTempStrings);
    for I := 0 to lTempStrings.Count - 1 do
    begin
      lID := SysUtils.Trim(lTempStrings[I]);
      lDisplayname := jsCommon.SplitStr(' ', lID);
      lID := SysUtils.Trim(lID);
      if (Length(lID) > 2) and (lID[1] = '<') and (lID[Length(lID)] = '>') then
        lID := StringUtils.Copy(lID, 2, Length(lID) - 2);
      lDisplayname := SysUtils.Trim(lDisplayName);
      if (Length(lDisplayname) > 2) and (lDisplayname[1] = '"') and (lDisplayname[Length(lDisplayname)] = '"') then
        lDisplayname := StringUtils.Copy(lDisplayname, 2, Length(lDisplayname) - 2);

      if lID = '' then
      begin
        lIDs.Clear;

        GetEmailByDisplayName(lDisplayName, lIDs);
        if lIDs.Count = 0 then
          GetIDByDisplayName(lDisplayName, lIDs);

        if lIDs.Count > 0 then
          lID := CheckID(lDisplayName, lIDs)
      end;

      if lID <> '' then
        Result := Result + SysUtils.Format('"%s" <%s>', [lDisplayName, lID])
      else
        Result := Result + lDisplayName;
      if I < lTempStrings.Count - 1 then
        Result := Result + ',';
    end;
  finally
    lIDs.Free;
    lTempStrings.Free;
  end;
end;

static function TMAILLIBRARY.CheckID(const ADisplayName: string; AIDs: TStrings): string;
begin
  if AIDs.Count > 0 then
  begin
    if AIDs.Count > 1 then
      Result := TSelectPersonForm.SelectPerson(ADisplayName, AIDs)
    else
      Result := AIDs[0];
  end
  else
    Result := ADisplayName;
end;

type
  TMailViewInfo = class(TObject)
    Full: Boolean;
    ReadOnly: Boolean;
    Data: TStream;
  end;

{ TCustomMailEditor }

constructor TCustomMailEditor.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FMails := TStringList.Create;
  TStringList(FMails).Duplicates := TDuplicates.dupError;
  TStringList(FMails).Sorted := True;
  FOpenedMails := TStringList.Create;
  TStringList(FOpenedMails).Duplicates := TDuplicates.dupError;
  TStringList(FOpenedMails).Sorted := True;
end;

destructor TCustomMailEditor.Destroy;
begin
  Clear;
  FMails.Free;
  FOpenedMails.Free;
  inherited Destroy;
end;

function TCustomMailEditor.GetMailInfo(const AMailID: string): TMailViewInfo;
begin
  Result := TMailViewInfo(FMails.Objects[FMails.IndexOf(AMailID)]);
end;

function TCustomMailEditor.MailStream(const AMailID: string): TStream;
begin
  Result := GetMailInfo(AMailID).Data;
  if Assigned(Result) then
    Result.Position := 0;
end;

{function TCustomMailEditor.GetMailPath(const AMailID: string): string;
var
  I: Integer;
begin
  I := FOpenedMails.IndexOf(AMailID);
  if I >= 0 then
    Result := TMailEditForm(FOpenedMails.Objects[I]).MailPath
  else
    Result := '';
end;}

procedure TCustomMailEditor.AddressBook(Sender: TObject; var SendTo, CC, BCC: string;
  var Changed: Boolean);
begin
  if Assigned(OnAddressBook) then
    OnAddressBook(Self, Sender as TWinControl, SendTo, CC, BCC, Changed);
end;

procedure TCustomMailEditor.GetAccountNames(Sender: TObject; AccountNames: TStrings);
begin
  if Assigned(OnGetAccountNames) then
    OnGetAccountNames(Sender, AccountNames);
end;

procedure TCustomMailEditor.GetOperUserInfo(Sender: TObject;
  out AOperUserID, AOperUserDisplayName: string);
begin
  if Assigned(OnGetOperUserInfo) then
    OnGetOperUserInfo(Sender, AOperUserID, AOperUserDisplayName);
end;

procedure TCustomMailEditor.About(Sender: TObject);
begin
  if Assigned(OnAbout) then
   OnAbout(Self, Sender as TWinControl);
end;

procedure TCustomMailEditor.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := TCloseAction.caFree;
  FOpenedMails.Delete(FOpenedMails.IndexOfObject(Sender));
end;

procedure TCustomMailEditor.Save(Sender: TObject; AStream: TStream;
  const APrepareSend: Boolean; var Saved: Boolean);
var
  I: Integer;
  lMailID, lNewMailID: string;
  lMailPath: string;
  lStream: TStream;
begin
  I := FOpenedMails.IndexOfObject(Sender);
  lMailID := FOpenedMails[I];
  lMailPath := TMailEditForm(FOpenedMails.Objects[I]).MailPath;
  lStream := MailStream(lMailID);
  lStream.Size := 0;
  lStream.CopyFrom(AStream, 0);
  if Assigned(OnSave) then
  begin
    lNewMailID := lMailID;
    OnSave(Self, lNewMailID, lMailPath, lStream, APrepareSend, Saved);
    if not SysUtils.SameText(lMailPath, TMailEditForm(FOpenedMails.Objects[I]).MailPath) then
      TMailEditForm(FOpenedMails.Objects[I]).MailPath := lMailPath;
    if not SysUtils.SameText(lNewMailID, lMailID) then
    begin
      FOpenedMails[I] := lNewMailID;
      TMailEditForm(FOpenedMails.Objects[I]).MailPath := lMailPath;
      AddMail(lNewMailID, AStream, True, False);
    end;
  end;
end;

procedure TCustomMailEditor.Sending(Sender: TObject; var CanSend: Boolean);
var
  lMailID: string;
begin
  if Assigned(OnSending) then
  begin
    lMailID := FOpenedMails[FOpenedMails.IndexOfObject(Sender)];
    OnSending(Self, lMailID, CanSend);
  end;
end;

procedure TCustomMailEditor.Sent(Sender: TObject);
var
  lMailID: string;
  lMailInfo: TMailViewInfo;
begin
  if Assigned(OnSent) then
  begin
    lMailID := FOpenedMails[FOpenedMails.IndexOfObject(Sender)];
    OnSent(Self, lMailID);
    lMailInfo := GetMailInfo(lMailID);
    lMailInfo.ReadOnly := True;
  end;
end;

procedure TCustomMailEditor.OnNewMail(Sender: TObject);
begin
  Edit(NewMail, '');
end;

function TCustomMailEditor.FullLoaded(const AMailID: string): Boolean;
begin
  Result := GetMailInfo(AMailID).Full;
end;

function TCustomMailEditor.HasHeader(const AMailID: string): Boolean;
var
  lStream: TStream;
begin
  lStream := MailStream(AMailID);
  Result := Assigned(lStream) and (lStream.Size <> 0);
end;

procedure TCustomMailEditor.AddMail(const AMailID: string; AStream: TStream;
  AFull, AReadOnly: Boolean);
var
  lMailInfo: TMailViewInfo;
begin
  lMailInfo := TMailViewInfo.Create;
  if Assigned(AStream) then
  begin
    lMailInfo.Data := THugeMemoryStream.Create(0);
    lMailInfo.Data.CopyFrom(AStream, 0);
    lMailInfo.Full := AFull;
  end
  else
  begin
    lMailInfo.Data := nil;
    lMailInfo.Full := False;
  end;
  lMailInfo.ReadOnly := AReadOnly;
  FMails.AddObject(AMailID, lMailInfo);
end;

procedure TCustomMailEditor.Clear;
var
  I: Integer;
  lForm: TMailEditForm;
  lMailInfo: TMailViewInfo;
begin
  for I := FOpenedMails.Count - 1 downto 0 do
  begin
    lForm := TMailEditForm(FOpenedMails.Objects[I]);
    lForm.Close;
  end;
  FOpenedMails.Clear;
  for I := Mails.Count - 1 downto 0 do
  begin
    lMailInfo := TMailViewInfo(Mails.Objects[I]);
    lMailInfo.Data.Free;
    lMailInfo.Free;
  end;
  Mails.Clear;
end;

procedure TCustomMailEditor.Delete(const AMailID: string);
var
  lDeleted: Boolean;
  lMailInfo: TMailViewInfo;
  I: Integer;
begin
  if Assigned(OnDelete) then
  begin
    lDeleted := False;
    OnDelete(Self, AMailID, lDeleted);
    if lDeleted then
    begin
      I := Mails.IndexOf(AMailID);
      lMailInfo := TMailViewInfo(Mails.Objects[I]);
      Mails.Delete(I);
      lMailInfo.Data.Free;
      lMailInfo.Free;
    end;
  end;
end;

{procedure TCustomMailEditor.DoEdit(const AMailID, AMailPath: string; const AReadOnly: Boolean; AReplayHeader: String = '');
var
  I: Integer;
  lMailInfo: TMailViewInfo;
  lForm: TMailEditForm;
begin
  I := FOpenedMails.IndexOf(AMailID);
  if I >= 0 then
    lForm := TMailEditForm(FOpenedMails.Objects[I])
  else
  begin
    LoadMail(AMailID, True);
    lMailInfo := GetMailInfo(AMailID);
    lForm := TMailEditForm.Create(BizSys.GetComponentContext(Self), Self);
    try
      lMailInfo.Data.Position := 0;
      lForm.LoadFromStream(lMailInfo.Data);
      lForm.ReadOnly := lMailInfo.ReadOnly;
      lForm.OnAddressBook := AddressBook;
      lForm.OnGetAccountNames := GetAccountNames;
      lForm.OnGetOperUserInfo := GetOperUserInfo;
      lForm.OnAbout := About;
      lForm.OnClose := FormClose;
      lForm.OnSave := Save;
      lForm.OnNew := OnNewMail;
      lForm.OnSending := Sending;
      lForm.OnSent := Sent;
      lForm.MailGUID := AMailID;
    except
      lForm.Free;
      raise;
    end;
    FOpenedMails.AddObject(AMailID, lForm);
  end;
  lForm.ReadOnly := AReadOnly;
  lForm.MailPath := AMailPath;
  lForm.Replayheader := AReplayHeader;
  lForm.Show;
  Borland.Delphi.Windows.PostMessage(lForm.Handle, Borland.Delphi.Messages.WM_Activate, 1, 0);
end;}
procedure TCustomMailEditor.DoEdit(const AMailID, AMailPath: string; const AReadOnly: Boolean; AReplayHeader: String = '');
begin
  DoEdit(AMailID, AMailPath, AReadOnly, AReplayHeader, '');
end;

procedure TCustomMailEditor.DoEdit(const AMailID, AMailPath: string; const AReadOnly: Boolean; AReplayHeader: String = ''; ATransmitMailGUID: String);
var
  I: Integer;
  lMailInfo: TMailViewInfo;
  lForm: TMailEditForm;
begin
  I := FOpenedMails.IndexOf(AMailID);
  if I >= 0 then
    lForm := TMailEditForm(FOpenedMails.Objects[I])
  else
  begin
    LoadMail(AMailID, False);
    lMailInfo := GetMailInfo(AMailID);
    lForm := TMailEditForm.Create(BizSys.GetComponentContext(Self), Self);
    try
      lMailInfo.Data.Position := 0;
      lForm.LoadFromStream(lMailInfo.Data);
      lForm.ReadOnly := lMailInfo.ReadOnly;
      lForm.OnAddressBook := AddressBook;
      lForm.OnGetAccountNames := GetAccountNames;
      lForm.OnGetOperUserInfo := GetOperUserInfo;
      lForm.OnAbout := About;
      lForm.OnClose := FormClose;
      lForm.OnSave := Save;
      lForm.OnNew := OnNewMail;
      lForm.OnSending := Sending;
      lForm.OnSent := Sent;
      lForm.MailGUID := AMailID;
      if ATransmitMailGUID <> '' then
      begin
        lForm.IsTransmitMail := True;
        lForm.TransmitMailGUID := ATransmitMailGUID;
      end;
    except
      lForm.Free;
      raise;
    end;
    FOpenedMails.AddObject(AMailID, lForm);
  end;
  lForm.ReadOnly := AReadOnly;
  lForm.MailPath := AMailPath;
  lForm.Replayheader := AReplayHeader;
  lForm.Show;
  Borland.Delphi.Windows.PostMessage(lForm.Handle, Borland.Delphi.Messages.WM_Activate, 1, 0);
end;

procedure TCustomMailEditor.Edit(const AMailID, AMailPath: string);
begin
  DoEdit(AMailID, AMailPath, False, '');
end;

procedure TCustomMailEditor.View(const AMailID, AMailPath: string);
begin
  DoEdit(AMailID, AMailPath, True, '');
end;

procedure TCustomMailEditor.GetMail(const AMailID: string; AStream: TStream);
begin
  LoadMail(AMailID, True);
  AStream.CopyFrom(MailStream(AMailID), 0);
end;

procedure TCustomMailEditor.GetMailHeader(const AMailID: string; AStream: TStream);
var
  lMailHeader: TMessageHeader;
begin
  LoadMail(AMailID, False);
  if FullLoaded(AMailID) then
  begin
    lMailHeader := TMessageHeader.Create;
    try
      lMailHeader.LoadFromStream(MailStream(AMailID));
      lMailHeader.SaveToStream(AStream);
    finally
      lMailHeader.Free;
    end;
  end
  else
    AStream.CopyFrom(MailStream(AMailID), 0);
end;

procedure TCustomMailEditor.LoadMail(const AMailID: string; AFull: Boolean);
var
  lMailInfo: TMailViewInfo;
begin
  lMailInfo := GetMailInfo(AMailID);
  if lMailInfo.Full or (HasHeader(AMailID) and not AFull) then
    Exit;
  if not Assigned(lMailInfo.Data) then
    lMailInfo.Data := THugeMemoryStream.Create(0);
  lMailInfo.Full := AFull;
  if Assigned(OnLoad) then
    OnLoad(Self, AMailID, lMailInfo.Data, lMailInfo.Full);
  if AFull and not lMailInfo.Full then
    raise Exception.Create('Load mail fail.');
  if lMailInfo.Data.Size = 0 then
  begin
    lMailInfo.Data.Free;
    lMailInfo.Data := nil;
    raise Exception.Create('Load mail fail.');
  end;
end;

function TCustomMailEditor.NewMail: string;
var
  lStream: TStream;
begin
  // Result := CreateMailID;                                                  // syd 2002-9-4
  Result := jsCommon.CreateGUIDStr;
  lStream := THugeMemoryStream.Create(0);
  try
    if Assigned(OnNew) then
      OnNew(Self, Result, lStream);
    AddMail(Result, lStream, True, False);
  finally
    lStream.Free;
  end;
end;

function TCustomMailEditor.ReplyMail(const AMailID, AMailPath: string): string;
const
  cReply = '回复: ';
//  cOldBody = '----------原始邮件-----------';
var
  lSendTo: string;  // , lSubject
  lMsg: TMessage;
  lMailInfo: TMailViewInfo;
  lHeader: String;
begin
  LoadMail(AMailID, False);
  lMailInfo := GetMailInfo(AMailID);

  lMsg := TMessage.Create(nil);
  try
    lMailInfo.Data.Position := 0;
    lMsg.LoadFromStream(lMailInfo.Data);
    with lMsg do
      lHeader := SysUtils.Format('<br>-----------原始邮件-----------<br>发件人: %s<br>收件人: %s<br>日期: %s<br>标题: %s<br><br>',
         [From, SendTo, Date, Subject]);
    lSendTo := lMsg.From;
//    lSubject := cReply + lMsg.Subject;
//    lMsg.Clear;  //这里还是不清的好,还是保留原邮件信息,SMP 20080824
    lMsg.From := lMsg.SendTo;
    lMsg.SendTo := lSendTo;
    lMsg.Subject := cReply + lMsg.Subject;
    Result := NewMail;
    lMailInfo := GetMailInfo(Result);
    lMailInfo.Data := THugeMemoryStream.Create(0);
    doReplay(lMsg);
    lMsg.SaveToStream(lMailInfo.Data);
  finally
    lMsg.Free;
  end;
  DoEdit(Result, AMailPath, False, lHeader);
end;

function TCustomMailEditor.TransmitMail(const AMailID, AMailPath: string): string;
const
  cTranmit = '转发: ';
var
  lMsg: TMessage;
  lMailInfo: TMailViewInfo;
//SMP Add 20080824
  lHeader: String;
begin
  LoadMail(AMailID, False);
  lMailInfo := GetMailInfo(AMailID);

  Result := NewMail;
  lMsg := TMessage.Create(nil);
  try
    lMailInfo.Data.Position := 0;
    lMsg.LoadFromStream(lMailInfo.Data);
    with lMsg do
      lHeader := SysUtils.Format('<br>-----------原始邮件-----------<br>发件人: %s<br>收件人: %s<br>日期: %s<br>标题: %s<br><br>',
         [From, SendTo, Date, Subject]);
    lMsg.SendTo := '';
    lMsg.Subject := SysUtils.Format('%s%s', [cTranmit, lMsg.Subject]);
    lMsg.From := '';
    lMailInfo := GetMailInfo(Result);
    lMailInfo.Data := THugeMemoryStream.Create(0);
    lMsg.SaveToStream(lMailInfo.Data);
  finally
    lMsg.Free;
  end;
  DoEdit(Result, AMailPath, False, lHeader, AMailID);
end;

procedure TCustomMailEditor.DoReplay(AMsg: TMessage);
begin
  if Assigned(FBeforeReply) then
    FBeforeReply(Self, AMsg);
end;

{ TMailViewer }

constructor TMailViewer.Create(AContext: TContext);
begin
  inherited Create(nil);
  FContext := AContext;
  FViewer := TMailDetail.Create(FContext);
  FViewer.Parent := Self;
  FViewer.Align := TAlign.alClient;
  FViewer.Show;
  //SetBounds(0, 0, 400, 300);
end;

procedure TMailViewer.Clear;
begin
  TMailDetail(FViewer).LoadFromStream(nil);
end;

procedure TMailViewer.LoadFromFile(const AFileName: string);
begin
  TMailDetail(FViewer).LoadFromFile(AFileName);
end;

procedure TMailViewer.LoadFromStream(AStream: TStream);
begin
  TMailDetail(FViewer).LoadFromStream(AStream);
end;

procedure TMailViewer.ViewMail(const AMailGUID: string; AContent: TStream;
  AAttachmentInfos: TList);
begin
  TMailDetail(FViewer).ViewMail(AMailGUID, AContent, AAttachmentInfos);
end;

function TMailViewer.GetLoadAttachContent: TLoadAttachContentEvent;
begin
  Result := TMailDetail(FViewer).OnLoadAttachContent;
end;

procedure TMailViewer.SetLoadAttachContent(
  const Value: TLoadAttachContentEvent);
begin
  if Assigned(Value) then
    TMailDetail(FViewer).OnLoadAttachContent := Value;
end;

function TMailViewer.GetWebBrowser: TWebBrowser;
begin
  Result := TMailDetail(FViewer).WebBrowser;
end;

const
  cAllReaded = '已读';
  cDepartReaded = '部分已读';
  cNoOneReaded = '未读';

function SearchMailParam(const AMatchGUIDs, AMatchSubject, AMatchFrom, AMatchTo,
  AMatchUIDL: string; const ABeginDate, AEndDate: TDateTime;
  const ALeastSize: Integer; const ASearchSubPath: Boolean): TSearchMailParam;
begin
  Result.MatchGUIDs := AMatchGUIDs;

  Result.MatchSubject := AMatchSubject;
  Result.MatchFrom :=  AMatchFrom;
  Result.MatchTo := AMatchTo;
  Result.MatchUIDL := AMatchUIDL;

  Result.BeginDate := ABeginDate;
  Result.EndDate := AEndDate;
  Result.LeastSize := ALeastSize;

  Result.SearchSubPath := ASearchSubPath;
end;

function SearchMailParam(const AMatchGUIDs: string;
  const ASearchSubPath: Boolean): TSearchMailParam;
begin
  Result := SearchMailParam(AMatchGUIDs, '', '', '', '', 0, 0, -1, ASearchSubPath);
end;

{procedure DeleteContinueBlank(var Str: string);
var
  I: Integer;
  S: string;
begin
  if Str <> '' then
  begin
    S := '';
    for I := Length(Str) downto 1 do
    begin
      if (Str[I] = #32) and (Str[I] = S) then
        Delete(Str, I, 1)
      else
        S := Str[I];
    end;
  end;
end;}

function GetExecPath: string;
begin
  Result := jsCommon.ModulePath;
end;

procedure FreeAndNil(var Obj: TObject);
var
  Temp: TObject;
begin
  Temp := Obj;
  Obj := nil;
  Temp.Free;
end;

{ TMailReference }

procedure TMailReference.Assign(Source: TPersistent);
var
  lSourceMailReference: TMailReference;
begin
  lSourceMailReference := TMailReference(Source);
  MailGUID    := lSourceMailReference.MailGUID;
  OwnerGUID   := lSourceMailReference.OwnerGUID;
  AccountGUID := lSourceMailReference.AccountGUID;
  PathGUID    := lSourceMailReference.PathGUID;
  Time        := lSourceMailReference.Time;
  Size        := lSourceMailReference.Size;
  State       := lSourceMailReference.State;

  MailInfo.Assign(lSourceMailReference.MailInfo);
end;

constructor TMailReference.Create;
begin
  inherited;

  MailGUID := '';
  OwnerGUID := '';
  AccountGUID := '';
  PathGUID := '';

  FMailInfo := TMailInfo.Create;
end;

destructor TMailReference.Destroy;
begin
  FMailInfo.Free;
  inherited;
end;

procedure TMailReference.SetMailInfo(const Value: TMailInfo);
begin
  FMailInfo.Assign(Value);
end;

{ TMailAttachment }

constructor TMailAttachment.Create;
begin
  inherited;
  MailGUID := '';
  FContent := THugeMemoryStream.Create(0);
end;

destructor TMailAttachment.Destroy;
begin
  FContent.Free;
  inherited;
end;

procedure TMailAttachment.Assign(Source: TPersistent);
var
  lSourceMailAttachment: TMailAttachment;
begin
  lSourceMailAttachment := TMailAttachment(Source);

  MailGUID := lSourceMailAttachment.MailGUID;
  Index := lSourceMailAttachment.Index;
  Content := lSourceMailAttachment.Content;

  inherited;
end;

procedure TMailAttachment.SetMailGUID(const Value: string);
begin
  FMailGUID := Value;
end;

function TMailAttachment.GetContent: TStream;
begin
  Result := FContent;
end;

procedure TMailAttachment.SetContent(const Value: TStream);
begin
  THugeMemoryStream(FContent).CopyFrom(Value, 0);
end;

function TMailAttachment.GetHashValue: string;
begin
  Result := jsCommon.MakeHashValue(Content);
end;

function TMailAttachment.GetSize: Integer;
begin
  Result := Content.Size;
end;

{ TMailPath }

constructor TMailPath.Create;
begin
  inherited;

  PathGUID := '';
  ParentGUID := '';
  OwnerGUID := '';
  DisplayName := '';
  ParentPath := '';
  FReadOnly := False;
end;

procedure TMailPath.Assign(Source: TPersistent);
var
  lSourceMailPath: TMailPath;
begin
  lSourceMailPath := TMailPath(Source);
  PathGUID := lSourceMailPath.PathGUID;
  ParentGUID := lSourceMailPath.ParentGUID;
  OwnerGUID := lSourceMailPath.OwnerGUID;
  DisplayName := lSourceMailPath.DisplayName;
  ParentPath := lSourceMailPath.ParentPath;
  FReadOnly := lSourceMailPath.ReadOnly;
end;

function TMailPath.GetFullPath: string;
begin
  if SameText(PathGUID, cGUID_Root) then
    Result := '\'
  else
    Result := ParentPath + PathGUID + FileUtils.PathDelim;
end;

{ TMailStorage }

procedure TMailStorage.AddMailInfoCommand(const AKey: TMailKey;
  AMailInfo: TMailInfo);
var
  lCommand: TMailInfoStorageCommand;
begin
  lCommand := TMailInfoStorageCommand.Create(AMailInfo);
  lCommand.Key := AKey;
  lCommand.Kind := TMailDataKind.mskInfo;
  FCommands.Add(lCommand);
end;

procedure TMailStorage.AddStreamCommand(const AKey: TMailKey;
  AMailAttachment: TMailAttachment);
var
  lCommand: TMailStreamStorageCommand;
begin
  lCommand := TMailStreamStorageCommand.Create(AMailAttachment);
  lCommand.Key := AKey;
  lCommand.Kind := TMailDataKind.mskStream;
  FCommands.Add(lCommand);
end;

procedure TMailStorage.AddMultiAttachmentCommand(const AKey: TMailKey;
  AStreamList: TList; const AStartIndex: Integer);
var
  lCommand: TMailMultiStreamStorageCommand;
begin
  lCommand := TMailMultiStreamStorageCommand.Create(AStreamList, AStartIndex);
  lCommand.Key := AKey;
  lCommand.Kind := TMailDataKind.mskStream;
  FCommands.Add(lCommand);
end;

procedure TMailStorage.AddMailRefCommand(const AKey: TMailKey;
  AMailRef: TMailReference);
var
  lCommand: TMailRefStorageCommand;
begin
  lCommand := TMailRefStorageCommand.Create(AMailRef);
  lCommand.Key := AKey;
  lCommand.Kind := TMailDataKind.mskReference;
  FCommands.Add(lCommand);
end;

procedure TMailStorage.AddMailMulitRefCommand(const AKey: TMailKey;
  ASearchMailParam: TSearchMailParam; AMailRefList: TStrings);
var
  lCommand: TMailMulitRefStorageCommand;
begin
  lCommand := TMailMulitRefStorageCommand.Create(ASearchMailParam, AMailRefList);
  lCommand.Key := AKey;
  lCommand.Kind := TMailDataKind.mskReference;
  FCommands.Add(lCommand);
end;

procedure TMailStorage.AddPathCommand(const AKey: TMailKey; AMailPath: TMailPath);
var
  lCommand: TMailPathStorageCommand;
begin
  lCommand := TMailPathStorageCommand.Create(AMailPath);
  lCommand.Key := AKey;
  lCommand.Kind := TMailDataKind.mskPath;
  FCommands.Add(lCommand);
end;

procedure TMailStorage.AddMulitPathCommand(const AKey: TMailKey;
  AMailPathList: TStrings);
var
  lCommand: TMailMulitPathStorageCommand;
begin
  lCommand := TMailMulitPathStorageCommand.Create(AMailPathList);
  lCommand.Key := AKey;
  lCommand.Kind := TMailDataKind.mskPath;
  FCommands.Add(lCommand);
end;

procedure TMailStorage.AddMovePathCommand(const AKey: TMailKey;
  const AOldParent, ANewParent, AOldPath, ANewPath: string);
var
  lCommand: TMailMovePathStorageCommand;
begin
  lCommand := TMailMovePathStorageCommand.Create(AOldParent, ANewParent, AOldPath, ANewPath);
  lCommand.Key := AKey;
  lCommand.Kind := TMailDataKind.mskPath;
  FCommands.Add(lCommand);
end;

procedure TMailStorage.AddMoveRefCommand(const AKey: TMailKey;
  const ANewPath: string);
var
  lCommand: TMailMoveRefStorageCommand;
begin
  lCommand := TMailMoveRefStorageCommand.Create(ANewPath);
  lCommand.Key := AKey;
  lCommand.Kind := TMailDataKind.mskReference;
  FCommands.Add(lCommand);
end;

procedure TMailStorage.AddAccountCommand(const Akey: TMailKey; AMailAccount: TMailAccount);
var
  lCommand: TMailAccountStorageCommand;
begin
  lCommand := TMailAccountStorageCommand.Create(AMailAccount);
  lCommand.Key := AKey;
  lCommand.Kind := TMailDataKind.mskAccount;
  FCommands.Add(lCommand);
end;

procedure TMailStorage.AddMulitAccountCommand(const AKey: TMailKey; AMailAccountList: TStrings);
var
  lCommand: TMailAccountStorageCommand;
begin
  lCommand := TMailMulitAccountStorageCommand.Create(AMailAccountList);
  lCommand.Key := AKey;
  lCommand.Kind := TMailDataKind.mskAccount;
  FCommands.Add(lCommand);
end;

constructor TMailStorage.Create(const AAction: TMailStorageAction);
begin
  inherited Create;
  FCommands := TObjectList.Create(True);
  FAction := AAction;
  Connection := TConnection.Create(nil);
  Connection.ConnectionString := cConnectionStr;
end;

destructor TMailStorage.Destroy;
begin
  Connection.Free;
  FCommands.Free;
  inherited;
end;

const
  MIS = 7;
  MSS = 8;
  MPS = 9;
  MRS = 10;
  MAS = 11;

procedure TMailStorage.DoExecute;
var
  I: Integer;
begin
  inherited;
  if Action in [TMailStorageAction.msaDelete, TMailStorageAction.msaMove] then Exit;
  for I := 0 to FCommands.Count - 1 do
    TMailStorageCommand(FCommands.Items[I]).DoStorage(Self);
end;

function TMailStorage.GetNeedSave: Boolean;
begin
  Result := Action in [TMailStorageAction.msaSave, TMailStorageAction.msaDelete,
    TMailStorageAction.msaMove];
end;

function TMailStorage.GetSQLBySearchParam(AKey: TMailKey;
  ASearchMailParam: TSearchMailParam): string;
const
  cSQL = '%s like ''%%%s%%''';
  cSelectMailSystemSQL = 'FGUID in (select FGUID from TMAILSYSTEM where (%s) and FCLIENTACCOUNT = ''%s'')';
  cSelectSelfAndAllSubPathSQL = 'FPATHGUID in (select b.FGUID from TMAILPATH a, TMAILPATH b where ' +
    'a.FGUID = ''%s'' and b.FPATH %s b.FGUID + ''\'' like a.FPATH %s a.FGUID + ''\%%'' and a.FCLIENTACCOUNT = ''%s'')';
  cDB2SelectSelfAndAllSubPathSQL = 'FPATHGUID in (select b.FGUID from TMAILPATH a, TMAILPATH b where ' +
    'a.FGUID = ''%s'' and  Upper(a.FPATH || a.FGUID) = Upper(SubStr((b.FPATH || b.FGUID), 1, Length(a.FPATH || a.FGUID) )) and a.FCLIENTACCOUNT = ''%s'')';
var
  lPathSQL: string;
  lGUIDsSQL: string;
  lUIDLSQL: string;
  lSubjectSQL: string;
  lFromSQL: string;
  lToSQL: string;
  lDateTimeSQL: string;
  lSizeSQL: string;
  lSymbol: string;
  lStrings: TStringList;
begin
  lPathSQL := '';
  if ASearchMailParam.SearchSubPath then
  begin
    if not ((AKey.Scope = '') or (AKey.Scope = '\')) then
    begin
      //lPathSQL := 'FPATHGUID like ''' + UpperCase(AKey.Scope) + '%'''
      if SameText(Connection.Meta.DataProvider, cDriver_MSSQL) or
        SameText(Connection.Meta.DataProvider, cDriver_SYBASE) then
        lSymbol := '+'
      else
        lSymbol := '||';
      if SameText(Connection.Meta.DataProvider, cDriver_DB2) then
        lPathSQL := Format(cDB2SelectSelfAndAllSubPathSQL, [UpperCase(AKey.Scope),
          BizSys.BizSystem.ClientAccount])
      else
        lPathSQL := Format(cSelectSelfAndAllSubPathSQL, [UpperCase(AKey.Scope),
          lSymbol, lSymbol, BizSys.BizSystem.ClientAccount]);
    end;
  end
  else
    lPathSQL := 'FPATHGUID = ''' + UpperCase(AKey.Scope) + '''';

  if ASearchMailParam.MatchGUIDs <> '' then
  begin
    lGUIDsSQL := '''' + AnsiReplaceStr(ASearchMailParam.MatchGUIDs, ';', ''',''') + '''';
    lGUIDsSQL := 'a.FGUID in (' + lGUIDsSQL + ')';
    if lPathSQL <> '' then
      Result := 'a.' + lPathSQL + ' and ' + lGUIDsSQL
    else
      Result := lGUIDsSQL;
  end
  else if ASearchMailParam.MatchUIDL <> '' then
  begin
    lUIDLSQL := 'a.' + Format(cSelectMailSystemSQL, [Format('%s = %s', ['FUIDL', SysUtils.QuotedStr(ASearchMailParam.MatchUIDL)]),
      BizSys.BizSystem.ClientAccount]);
    if lPathSQL <> '' then
      Result := 'a.' + lPathSQL + ' and ' + lUIDLSQL
    else
      Result := lUIDLSQL;
  end
  else
  begin
    if ASearchMailParam.MatchSubject <> '' then
      lSubjectSQL := Format(cSelectMailSystemSQL, [Format(cSQL, ['FSUBJECT', ASearchMailParam.MatchSubject]),
        BizSys.BizSystem.ClientAccount])
    else
      lSubjectSQL := '';

    if ASearchMailParam.MatchFrom <> '' then
      lFromSQL := Format(cSelectMailSystemSQL, [Format(cSQL, ['FFROM', ASearchMailParam.MatchFrom]),
        BizSys.BizSystem.ClientAccount])
    else
      lFromSQL := '';

    if ASearchMailParam.MatchTo <> '' then
      lToSQL := Format(cSelectMailSystemSQL, [Format(cSQL, ['FTOS', ASearchMailParam.MatchTo]),
        BizSys.BizSystem.ClientAccount])
    else
      lToSQL := '';

    if (ASearchMailParam.BeginDate = 0) and (ASearchMailParam.EndDate = 0) then
      lDateTimeSQL := ''
    else if ASearchMailParam.BeginDate = 0 then
      lDateTimeSQL := GetDateSQL(ASearchMailParam.EndDate, 'FTIME', '<=')
    else if ASearchMailParam.EndDate = 0 then
      lDateTimeSQL := GetDateSQL(ASearchMailParam.BeginDate, 'FTIME', '>=')
    else
      lDateTimeSQL := GetDateSQL(ASearchMailParam.EndDate, 'FTIME', '<=') + ' and ' +
        GetDateSQL(ASearchMailParam.BeginDate, 'FTIME', '>=');

    if ASearchMailParam.LeastSize = 0 then
      lSizeSQL := ''
    else
      lSizeSQL := 'FSIZE >= ' + IntToStr(ASearchMailParam.LeastSize);

    lStrings := TStringList.Create;
    try
      if lPathSQL <> '' then
        lStrings.Add(lPathSQL);
      if lSubjectSQL <> '' then
        lStrings.Add(lSubjectSQL);
      if lFromSQL <> '' then
        lStrings.Add(lFromSQL);
      if lToSQL <> '' then
        lStrings.Add(lToSQL);
      if lDateTimeSQL <> '' then
        lStrings.Add(lDateTimeSQL);
      if lSizeSQL <> '' then
        lStrings.Add(lSizeSQL);

      Result := FormatAndUniteStr(' and ', '(a.%s)', lStrings);
    finally
      lStrings.Free;
    end;
  end;
end;

function TMailStorage.GetSQLFromCommandList(ACommands: TObjectList;
  const AMailDataKind: TMailDataKind; const AFieldProfix: string): string;
var
  I: Integer;
  lCommand: TMailStorageCommand;
  lStrings: TStringList;
  lScope, lOwnerGUID, lPath, lOperate: string;
  S: string;
begin
  lStrings := TStringList.Create;
  try
    for I := 0 to ACommands.Count - 1 do
    begin
      lCommand := TMailStorageCommand(ACommands.Items[I]);
      if lCommand.Kind <> AMailDataKind then continue;

      lScope := lCommand.Key.Scope;
      lOwnerGUID := lCommand.Key.OwnerGUID;
      lPath := lCommand.Key.PathGUID;
      lOperate := ' and ';
      //如果是搜索动作,那么只能够有一个Command
      if lCommand.Key.ScopeKind = TMailScopeKind.skPath then
      begin
        Assert(ACommands.Count = 1, '一次只能够进行一次搜索');
        case lCommand.Kind of
          TMailDataKind.mskReference:
          begin
            Assert(lScope <> '', '检索条件不能够为空');
            lScope := GetSQLBySearchParam(lCommand.Key,
              TMailMulitRefStorageCommand(lCommand).FSearchMailParam);
          end;
          TMailDataKind.mskPath, TMailDataKind.mskAccount:
          begin
            Assert(lScope = '', '');
            lOperate := '';
          end;
        else
          Assert(False, '不支持的操作对象');
        end;
      end
      else
      begin
        Assert(lScope <> '', '检索条件不能够为空');
        lScope := AFieldProfix + 'FGUID = ''' + lScope + '''';
        S := '';
        if AMailDataKind = TMailDataKind.mskStream then
        begin
          if lCommand is TMailMultiStreamStorageCommand then
            S := Format(' and FINDEX >= %d ',
              [TMailMultiStreamStorageCommand(lCommand).FStartIndex])
          else if lCommand is TMailStreamStorageCommand then
            S := Format(' and FINDEX = %d ',
              [TMailStreamStorageCommand(lCommand).FMailAttachment.Index])
          else
            raise Exception.Create('错误的AttachmentCommand');
        end;
        {else if AMailDataKind = TMailDataKind.mskReference then
        begin
          if lCommand is TMailMoveRefStorageCommand then
          begin
            Assert(ACommands.Count = 1, '一次只能够移动一个邮件');
            S := Format(' and FPATHGUID = ''%s'' ', [TMailMoveRefStorageCommand(lCommand).FOldPath]);
          end;
        end;}
        lScope := lScope + S;
      end;

      if lOwnerGUID <> '' then
        lOwnerGUID := lOperate + 'UPPER(' + AFieldProfix + 'FOWNERGUID) = ''' + SysUtils.UpperCase(lOwnerGUID) + '''';
      S := lScope + lOwnerGUID;
      Assert(S <> '', '');
      if lPath <> '' then
        S := S + ' and ' + AFieldProfix + 'FPATHGUID = ''' + lPath + '''';
      lStrings.Add(S);
    end;
    Result := FormatAndUniteStr(' or ', '(%s)', lStrings);
  finally
    lStrings.Free;
  end;
end;
回复 支持 反对

使用道具 举报

 楼主| 发表于 2008-2-29 13:55:40 | 显示全部楼层
邮件库(下)
procedure TMailStorage.PrepareSQLAndMaps;
const
  cSelectMailSQL = 'select * from TMAILSYSTEM where (%s) and FCLIENTACCOUNT = ''%s''';

  cDeleteMailWhereSQL = 'where FGUID not in (select DISTINCT FGUID from TMAILREFERENCE)';
  cDeleteMailSQL = 'delete from TMAILSYSTEM ' + cDeleteMailWhereSQL;
  cDeleteMailStreamSQL = 'delete from TMAILCONTENT ' + cDeleteMailWhereSQL;

  cSelectStreamSQL = 'select * from TMAILCONTENT where (%s) and FCLIENTACCOUNT = ''%s'' ORDER BY FGUID, FINDEX, FSPLITNO';
  cDeleteStreamSQL = 'delete from TMAILCONTENT where (%s) and FCLIENTACCOUNT = ''%s''';
  cSelectNULLStreamSQL = 'select * from TMAILCONTENT where 1 <> 1';

  //如果是Load状态,需要同时加载MailInfo的内容
  cSelectMailRefSQL = 'select * from TMAILREFERENCE where (%s) and FCLIENTACCOUNT = ''%s''';
  cSelectAllMailRefSQL = 'select a.*, b.* from TMAILREFERENCE a, TMAILSYSTEM b ' +
    'where a.FGUID = b.FGUID and (%s) and b.FCLIENTACCOUNT = ''%s''';//a.FGUID in (select FGUID from TMAILREFERENCE where %s)';
  cDeleteMailRefSQL = 'delete from TMAILREFERENCE where (%s) and FCLIENTACCOUNT = ''%s''';
  cMoveMailRefSQL = 'update TMAILREFERENCE set FPATHGUID = ''%s'' where (%s) and FCLIENTACCOUNT = ''%s''';

  cSelectPathSQL = 'select * from TMAILPATH where (%s) and FCLIENTACCOUNT = ''%s''';

  cSelectSelfAndAllSubPathWhereSQL = 'select b.FGUID from TMAILPATH a, TMAILPATH b where ' +
    'a.FGUID in (select FGUID from TMAILPATH where %s) and b.FPATH %s b.FGUID %s ''\'' like a.FPATH %s a.FGUID %s ''\%%'' and a.FCLIENTACCOUNT = ''%s''';
  cDB2SelectSelfAndAllSubPathWhereSQL = 'select b.FGUID from TMAILPATH a, TMAILPATH b where ' +
    'a.FGUID in (select FGUID from TMAILPATH where %s) and  Upper(a.FPATH || a.FGUID) = Upper(SubStr((b.FPATH || b.FGUID), 1, Length(a.FPATH || a.FGUID))) and a.FCLIENTACCOUNT = ''%s''';

  cDeletePathSQL = 'delete from TMAILPATH where FGUID in (%s) and FCLIENTACCOUNT = ''%s''';
  cDeleteRefByPathSQL = 'delete from TMAILREFERENCE where FPATHGUID in (%s) and FCLIENTACCOUNT = ''%s''';
  cMovePathSQL = 'update TMAILPATH set FPARENTGUID = ''%s'', FPATH = ''%s'' where( %s) and FCLIENTACCOUNT = ''%s''';
  cUpdatePathANSISQL = 'update TMAILPATH set FPATH = ''%s'' || SUBSTRING(FPATH from %d for 255) where FPATH like ''%s'' and FCLIENTACCOUNT = ''%s''';
  cUpdatePathMSSQLSQL = 'update TMAILPATH set FPATH = ''%s'' + SUBSTRING(FPATH, %d, 255) where FPATH like ''%s'' and FCLIENTACCOUNT = ''%s''';
  cUpdatePathOracleSQL = 'update TMAILPATH set FPATH = ''%s'' || SUBSTR(FPATH, %d, 255) where FPATH like ''%s'' and FCLIENTACCOUNT = ''%s''';
  cUpdatePathDB2SQL = 'update TMAILPATH set FPATH = ''%s'' || SUBSTR(FPATH, %d, Length(FPATH)) where FPATH like ''%s'' and FCLIENTACCOUNT = ''%s''';

  cSelectAccountSQL = 'select * from TMAILACCOUNTS where (%s) and FCLIENTACCOUNT = ''%s''';
  cDeleteAccountSQL = 'delete from TMAILACCOUNTS where (%s) and FCLIENTACCOUNT = ''%s''';
var
  lSymbol: string;
  lOldPath, lNewPath: string;
  lMailInfoSQL, lMailStreamSQL, lMailReferenceSQL, lMailPathSQL, lMailAccountSQL: string;
  lMISelectSQL, lMIDeleteSQL, lMISDeleteSQL: string;
  lMSSelectSQL, lMSDeleteSQL: string;
  lMRSelectSQL, lMRDeleteSQL, lMRMoveSQL: string;
  lMPSelectSQL, lMPDeleteSQL, lMPRDeleteSQL, lMPMoveSQL, lMPMoveSQLEx, lDeleteStr: string;
  lMASelectSQL, lMADeleteSQL: string;
  lFieldPrefix: string;
  lCommand: TMailMovePathStorageCommand;
begin
  if FCommands.Count = 0 then Exit;

  lMISelectSQL := '';
  lMIDeleteSQL := '';
  lMISDeleteSQL := '';
  lMSSelectSQL := '';
  lMSDeleteSQL := '';
  lMRSelectSQL := '';
  lMRDeleteSQL := '';
  lMRMoveSQL := '';
  lMPSelectSQL := '';
  lMPDeleteSQL := '';
  lMPMoveSQL := '';
  lMPMoveSQLEx := '';
  lMASelectSQL := '';
  lMADeleteSQL := '';



  lMailInfoSQL := GetSQLFromCommandList(FCommands, TMailDataKind.mskInfo, '');
  lMailStreamSQL := GetSQLFromCommandList(FCommands, TMailDataKind.mskStream, '');
  if Action = TMailStorageAction.msaLoad then
    lFieldPrefix := 'a.'
  else
    lFieldPrefix := '';
  lMailReferenceSQL := GetSQLFromCommandList(FCommands,
    TMailDataKind.mskReference, lFieldPrefix);
  lMailPathSQL := GetSQLFromCommandList(FCommands, TMailDataKind.mskPath, '');
  lMailAccountSQL := GetSQLFromCommandList(FCommands, TMailDataKind.mskAccount, '');

  if lMailInfoSQL <> '' then
  begin
    if Action in [TMailStorageAction.msaDelete, TMailStorageAction.msaMove] then
      Assert(False, '不能够移动或直接删除邮件实体')
    else
      lMISelectSQL := Format(cSelectMailSQL, [lMailInfoSQL, BizSys.BizSystem.ClientAccount]);
  end;

  if lMailStreamSQL <> '' then
  begin
    case Action of
      TMailStorageAction.msaLoad:
        lMSSelectSQL := Format(cSelectStreamSQL, [lMailStreamSQL, BizSys.BizSystem.ClientAccount]);
      TMailStorageAction.msaSave:
      begin
        lMSDeleteSQL := Format(cDeleteStreamSQL, [lMailStreamSQL, BizSys.BizSystem.ClientAccount]);
        lMSSelectSQL := cSelectNULLStreamSQL;
      end;
      TMailStorageAction.msaDelete:
        lMSDeleteSQL := Format(cDeleteStreamSQL, [lMailStreamSQL, BizSys.BizSystem.ClientAccount]);
    else
      Assert(False, '不能够移动邮件内容或者附件')
    end;
  end;

  if lMailReferenceSQL <> '' then
  begin
    case Action of
      TMailStorageAction.msaLoad:
        lMRSelectSQL := Format(cSelectAllMailRefSQL, [lMailReferenceSQL, BizSys.BizSystem.ClientAccount]);
      TMailStorageAction.msaSave:
        lMRSelectSQL := Format(cSelectMailRefSQL, [lMailReferenceSQL, BizSys.BizSystem.ClientAccount]);
      TMailStorageAction.msaDelete:
      begin
        lMRDeleteSQL := Format(cDeleteMailRefSQL, [lMailReferenceSQL, BizSys.BizSystem.ClientAccount]);
        //删除没有引用的邮件实体
        lMISDeleteSQL := cDeleteMailStreamSQL;
        lMIDeleteSQL := cDeleteMailSQL;
      end;
      TMailStorageAction.msaMove:
        lMRMoveSQL := Format(cMoveMailRefSQL,
          [TMailMoveRefStorageCommand(FCommands.Items[0]).FNewPath, lMailReferenceSQL, BizSys.BizSystem.ClientAccount]);
    end;
  end;

  //Path
  if lMailPathSQL <> '' then
  begin
    lCommand := TMailMovePathStorageCommand(FCommands.Items[0]);
    case Action of
      TMailStorageAction.msaLoad, TMailStorageAction.msaSave:
        lMPSelectSQL := Format(cSelectPathSQL, [lMailPathSQL, BizSys.BizSystem.ClientAccount]);
      TMailStorageAction.msaDelete:
      begin
        if SameText(Connection.Meta.DataProvider, cDriver_MSSQL) or
          SameText(Connection.Meta.DataProvider, cDriver_SYBASE) then
          lSymbol := '+'
        else
          lSymbol := '||';

        if SameText(Connection.Meta.DataProvider, cDriver_DB2) then
          lDeleteStr := Format(cDB2SelectSelfAndAllSubPathWhereSQL, [lMailPathSQL, BizSys.BizSystem.ClientAccount])
        else
          lDeleteStr := Format(cSelectSelfAndAllSubPathWhereSQL, [lMailPathSQL, lSymbol, lSymbol, lSymbol, lSymbol, BizSys.BizSystem.ClientAccount]);
        //要先删除下面的引用
        lMPRDeleteSQL := Format(cDeleteRefByPathSQL, [lDeleteStr, BizSys.BizSystem.ClientAccount]);
        //删除Path
        lMPDeleteSQL := Format(cDeletePathSQL, [lDeleteStr, BizSys.BizSystem.ClientAccount]);
        //删除没有引用的邮件实体
        lMISDeleteSQL := cDeleteMailStreamSQL;
        lMIDeleteSQL := cDeleteMailSQL;
      end;
      TMailStorageAction.msaMove:
      begin
        lMPMoveSQL := Format(cMovePathSQL, [lCommand.FNewParent, lCommand.FNewPath, lMailPathSQL, BizSys.BizSystem.ClientAccount]);
        if SameText(Connection.Meta.DataProvider, cDriver_ORACLE) then
          lMPMoveSQLEx := cUpdatePathOracleSQL
        else if SameText(Connection.Meta.DataProvider, cDriver_DB2) then
          lMPMoveSQLEx := cUpdatePathDB2SQL
        else if SameText(Connection.Meta.DataProvider, cDriver_MSSQL) then
          lMPMoveSQLEx := cUpdatePathMSSQLSQL
        else if SameText(Connection.Meta.DataProvider, cDriver_SQLITE) then
          lMPMoveSQLEx := cUpdatePathOracleSQL
        else if SameText(Connection.Meta.DataProvider, cDriver_Sybase) then
          lMPMoveSQLEx := cUpdatePathMSSQLSQL
        else
          lMPMoveSQLEx := cUpdatePathANSISQL;

        lNewPath := lCommand.FNewPath + lCommand.Key.Scope + '\';
        lOldPath := lCommand.FOldPath + lCommand.Key.Scope + '\%';
        lMPMoveSQLEx := Format(lMPMoveSQLEx, [lNewPath, Length(lOldPath), lOldPath, BizSys.BizSystem.ClientAccount]);
      end;
    end;
  end;

  //Account
  if lMailAccountSQL <> '' then
  begin
    case Action of
      TMailStorageAction.msaLoad, TMailStorageAction.msaSave:
        lMASelectSQL := Format(cSelectAccountSQL, [lMailAccountSQL, BizSys.BizSystem.ClientAccount]);
      TMailStorageAction.msaDelete:
        lMADeleteSQL := Format(cDeleteAccountSQL, [lMailAccountSQL, BizSys.BizSystem.ClientAccount]);
    else
      Assert(False, '不能够移动邮件帐号')
    end;
  end;

  //删除时的顺序:Stream, Refrence, Path, Info, Account
  AddSQLAndMap(lMSDeleteSQL, [], lMSDeleteSQL <> '');

  AddSQLAndMap(lMRDeleteSQL, [], lMRDeleteSQL <> '');

  AddSQLAndMap(lMPRDeleteSQL, [], lMPRDeleteSQL <> '');
  AddSQLAndMap(lMPDeleteSQL, [], lMPDeleteSQL <> '');

  AddSQLAndMap(lMISDeleteSQL, [], lMISDeleteSQL <> '');
  AddSQLAndMap(lMIDeleteSQL, [], lMIDeleteSQL <> '');

  AddSQLAndMap(lMADeleteSQL, [], lMADeleteSQL <> '');

  //创建和修改时先操作Info
  AddSQLAndMap(lMISelectSQL, [], False);

  AddSQLAndMap(lMSSelectSQL, [], False);

  AddSQLAndMap(lMPSelectSQL, [], False);

  AddSQLAndMap(lMRSelectSQL, [], False);

  AddSQLAndMap(lMASelectSQL, [], False);

  //移动的SQL
  AddSQLAndMap(lMRMoveSQL, [], lMRMoveSQL <> '');
  AddSQLAndMap(lMPMoveSQL, [], lMPMoveSQL <> '');
  AddSQLAndMap(lMPMoveSQLEx, [], lMPMoveSQLEx <> '');
end;

function TMailStorage.GetDateSQL(const ADateTime: TDateTime;
  const AFieldName, ASymbol: string): string;
begin
  Result := Format('%s %s ''%s''', [AFieldName, ASymbol, FormatDateTime('yyyy-mm-dd 00:00:00', ADateTime)]);
end;

{ TMailStorageCommand }

procedure TMailStorageCommand.DoLoad(AStorage: TMailStorage);
begin
  { nothing to do }
end;

procedure TMailStorageCommand.DoSave(AStorage: TMailStorage);
begin
  { nothing to do }
end;

procedure TMailStorageCommand.DoStorage(AStorage: TMailStorage);
begin
  with AStorage do
    if Assigned(DataSets[GetDataSetIndex]) then
    begin
      case Action of
        { 从DataSet中取数据 }
        TMailStorageAction.msaLoad: DoLoad(AStorage);
        { 设置数据到DataSet中 }
        TMailStorageAction.msaSave: DoSave(AStorage);
      end;
    end;
end;

procedure SetMailInfoByDataSet(AMailInfo: TMailInfo; ADataSet: TDataSet);
begin
  AMailInfo.MailGUID := ADataSet.FieldByName('FGUID').AsString;
  AMailInfo.UIDL := ADataSet.FieldByName('FUIDL').AsString;
  AMailInfo.Size := ADataSet.FieldByName('FSIZE').AsInteger;
  AMailInfo.HasAttach := Boolean(ADataSet.FieldByName('FHASATTACH').AsInteger);
  AMailInfo.Tos := ADataSet.FieldByName('FTOS').AsString;
  AMailInfo.From := ADataSet.FieldByName('FFROM').AsString;
  AMailInfo.Cc := ADataSet.FieldByName('FCC').AsString;
  AMailInfo.Bcc := ADataSet.FieldByName('FBCC').AsString;
  AMailInfo.ReplyTo := ADataSet.FieldByName('FREPLYTO').AsString;
  AMailInfo.Subject := ADataSet.FieldByName('FSUBJECT').AsString;
  AMailInfo.Priority := ADataSet.FieldByName('FPRIORITY').AsInteger;
  AMailInfo.Date := ADataSet.FieldByName('FDATE').AsDateTime;
  AMailInfo.HeaderText := ADataSet.FieldByName('FHEADER').AsString;
  AMailInfo.AttachmentInfos.Text := ADataSet.FieldByName('FATTACHMENTINFOS').AsString;
end;

{ TMailInfoStorageCommand }

constructor TMailInfoStorageCommand.Create(AMailInfo: TMailInfo);
begin
  inherited Create;
  FMailInfo := AMailInfo;
end;

procedure TMailInfoStorageCommand.DoLoad(AStorage: TMailStorage);
var
  lDataSet: TDataSet;
begin
  if Key.ScopeKind = TMailScopeKind.skGUID then
  begin
    lDataSet := AStorage.DataSets[GetDataSetIndex];
    if lDataSet.Locate('FGUID', Key.Scope, []) then
      SetMailInfoByDataSet(FMailInfo, lDataSet);
  end
  else
    Assert(False, '没有支持的方法');
end;

procedure TMailInfoStorageCommand.DoSave(AStorage: TMailStorage);
var
  lDataSet: TDataSet;
begin
  if Key.ScopeKind = TMailScopeKind.skGUID then
  begin
    lDataSet := AStorage.DataSets[GetDataSetIndex];
    if lDataSet.Locate('FGUID', Key.Scope, []) then
      lDataSet.Edit
    else
    begin
      lDataSet.Append;
      lDataSet.FieldByName('FCLIENTACCOUNT').AsString := BizSys.BizSystem.ClientAccount;
    end;

    lDataSet.FieldByName('FGUID').AsString := FMailInfo.MailGUID;
    lDataSet.FieldByName('FUIDL').AsString := FMailInfo.UIDL;
    lDataSet.FieldByName('FSIZE').AsInteger := FMailInfo.Size;
    lDataSet.FieldByName('FHASATTACH').AsInteger := Ord(FMailInfo.HasAttach);
    lDataSet.FieldByName('FTOS').AsString := FMailInfo.Tos;
    lDataSet.FieldByName('FFROM').AsString := FMailInfo.From;
    lDataSet.FieldByName('FCC').AsString := FMailInfo.Cc;
    lDataSet.FieldByName('FBCC').AsString := FMailInfo.Bcc;
    lDataSet.FieldByName('FREPLYTO').AsString := FMailInfo.ReplyTo;
    lDataSet.FieldByName('FSUBJECT').AsString := FMailInfo.Subject;
    lDataSet.FieldByName('FPRIORITY').AsInteger := FMailInfo.Priority;
    if FMailInfo.Date = 0 then
      lDataSet.FieldByName('FDATE').AsDateTime := SysUtils.Now
    else
      lDataSet.FieldByName('FDATE').AsDateTime := FMailInfo.Date;
    lDataSet.FieldByName('FHEADER').AsString := FMailInfo.HeaderText;
    lDataSet.FieldByName('FATTACHMENTINFOS').AsString := FMailInfo.AttachmentInfos.Text;

    lDataSet.Post;
  end
  else
    Assert(False, '没有支持的方法');
end;

function TMailInfoStorageCommand.GetDataSetIndex: Integer;
begin
  Result := MIS;
end;

{ TMailStreamStorageCommand }

constructor TMailStreamStorageCommand.Create(AMailAttachment: TMailAttachment);
begin
  inherited Create;
  FMailAttachment := AMailAttachment;
end;

procedure TMailStreamStorageCommand.DoLoad(AStorage: TMailStorage);
begin
  SetStream(AStorage.DataSets[GetDataSetIndex], FMailAttachment.Index,
    FMailAttachment.Content);
end;

procedure TMailStreamStorageCommand.DoSave(AStorage: TMailStorage);
begin

  SetDataSet(AStorage.DataSets[GetDataSetIndex], FMailAttachment.Index,
    FMailAttachment.Content);
end;

procedure TMailStreamStorageCommand.SetStream(ADataSet: TDataSet;
  const AIndex: Integer; AStream: TStream);
var
  lArrObject: array of Object;
  lObject: Object;
begin
  SetLength(lArrObject, 2);
  lArrObject[0] := Key.Scope;
  lArrObject[1] := AIndex;

  ReadSplitFieldCntFromDataSet(ADataSet, 'FGUID;FINDEX', 'FSPLITNO', 'FCONTENT',
    '', lArrObject, lObject, AStream, True);
end;

procedure TMailStreamStorageCommand.SetDataSet(ADataSet: TDataSet;
  const AIndex: Integer; AStream: TStream);
var
  lObject: Object;
  lArrObject: array of Object;
begin
  if AStream.Size > 0 then
  begin
    SetLength(lArrObject, 2);
    lArrObject[0] := Key.Scope;
    lArrObject[1] := AIndex;
    lObject := MakeHashValue(AStream);

    WriteSplitFieldCntToDataSet(ADataSet, 'FGUID;FINDEX', 'FSPLITNO',
      'FCONTENT', 'FHASHVALUE', lArrObject, lObject, AStream, True, True);
  end;
end;

function TMailStreamStorageCommand.GetDataSetIndex: Integer;
begin
  Result := MSS;
end;

{ TMailMultiStreamStorageCommand }

constructor TMailMultiStreamStorageCommand.Create(AStreamList: TList;
  const AStartIndex: Integer);
begin
  inherited Create;
  FStreamList := AStreamList;
  FStartIndex := AStartIndex;
end;

procedure TMailMultiStreamStorageCommand.DoLoad(AStorage: TMailStorage);
begin
  Assert(False, '邮件附件不能够成批读取');
end;

procedure TMailMultiStreamStorageCommand.DoSave(AStorage: TMailStorage);
var
  I: Integer;
  lDataSet: TDataSet;
begin
  lDataSet := AStorage.DataSets[GetDataSetIndex];
  for I := 0 to FStreamList.Count - 1 do
    SetDataSet(lDataSet, FStartIndex + I, TStream(FStreamList[I]));
end;

{ TMailRefStorageCommand }

constructor TMailRefStorageCommand.Create(AMailRef: TMailReference);
begin
  inherited Create;
  FMailRef := AMailRef;
end;

procedure TMailRefStorageCommand.SetMailRef(ADataSet: TDataSet; AMailRef: TMailReference);
begin
  AMailRef.MailGUID := ADataSet.FieldByName('FGUID').AsString;
  AMailRef.OwnerGUID := ADataSet.FieldByName('FOWNERGUID').AsString;
  AMailRef.AccountGUID := ADataSet.FieldByName('FACCOUNTGUID').AsString;
  AMailRef.PathGUID := ADataSet.FieldByName('FPATHGUID').AsString;
  AMailRef.Time := ADataSet.FieldByName('FTIME').AsDateTime;
  AMailRef.Size := ADataSet.FieldByName('FSIZE').AsInteger;
  AMailRef.State := ADataSet.FieldByName('FSTATE').AsInteger;

  SetMailInfoByDataSet(AMailRef.MailInfo, ADataSet);
end;

procedure TMailRefStorageCommand.DoLoad(AStorage: TMailStorage);
var
  lArrObject: array of Object;
  lDataSet: TDataSet;
begin
  if Key.ScopeKind = TMailScopeKind.skGUID then
  begin
    lDataSet := AStorage.DataSets[GetDataSetIndex];

    SetLength(lArrObject, 2);
    lArrObject[0] := Key.Scope;
    lArrObject[1] := Key.OwnerGUID;

    if lDataSet.Locate('FGUID;FOWNERGUID', lArrObject, [TLocateOption.loCaseInsensitive]) then
      SetMailRef(lDataSet, FMailRef);
  end
  else
    Assert(False, '没有支持的方法');
end;

procedure TMailRefStorageCommand.DoSave(AStorage: TMailStorage);
var
  lDataSet: TDataSet;
  lArrObject: array of Object;
begin
  if Key.ScopeKind = TMailScopeKind.skGUID then
  begin
    lDataSet := AStorage.DataSets[GetDataSetIndex];

    SetLength(lArrObject, 3);
    lArrObject[0] := Key.Scope;
    lArrObject[1] := Key.OwnerGUID;
    lArrObject[2] := Key.PathGUID;
    //SMP ADD 原来的写法突然出错异常信息,记录已经被修改了,像下面这样写就OK了 20060928
    lDataSet.UpdateOptions.UpdateMode := TUpdateMode.upWhereKeyOnly;
    lDataSet.UpdateOptions.Fields.Clear;
    with lDataSet.UpdateOptions.Fields.Add do
    begin
      Name := 'FGUID';
      ProviderFlags := [TProviderFlag.pfInKey];
    end;
    with lDataSet.UpdateOptions.Fields.Add do
    begin
      Name := 'FOWNERGUID';
      ProviderFlags := [TProviderFlag.pfInKey];
    end;
    with lDataSet.UpdateOptions.Fields.Add do
    begin
      Name := 'FPATHGUID';
      ProviderFlags := [TProviderFlag.pfInKey];
    end;
    with lDataSet.UpdateOptions.Fields.Add do
    begin
      Name := 'FCLIENTACCOUNT';
      ProviderFlags := [TProviderFlag.pfInKey];
    end;
    //END
    if lDataSet.Locate('FGUID;FOWNERGUID;FPATHGUID', lArrObject, [TLocateOption.loCaseInsensitive]) then
      lDataSet.Edit
    else
    begin
      lDataSet.Append;
      lDataSet.FieldByName('FCLIENTACCOUNT').AsString := BizSys.BizSystem.ClientAccount;
    end;

    lDataSet.FieldByName('FGUID').AsString := FMailRef.MailGUID;
    lDataSet.FieldByName('FOWNERGUID').AsString := FMailRef.OwnerGUID;
    lDataSet.FieldByName('FACCOUNTGUID').AsString := FMailRef.AccountGUID;
    lDataSet.FieldByName('FPATHGUID').AsString := FMailRef.PathGUID;
    lDataSet.FieldByName('FTIME').AsDateTime := FMailRef.Time;
    lDataSet.FieldByName('FSIZE').AsInteger := FMailRef.Size;
    lDataSet.FieldByName('FSTATE').AsInteger := FMailRef.State;

    lDataSet.Post;
  end
  else
    Assert(False, '没有支持的方法');
end;

function TMailRefStorageCommand.GetDataSetIndex: Integer;
begin
  Result := MRS;
end;

{ TMailMulitRefStorageCommand }

constructor TMailMulitRefStorageCommand.Create(
  ASearchMailParam: TSearchMailParam; AMailRefList: TStrings);
begin
  inherited Create;
  FSearchMailParam := ASearchMailParam;
  FMailRefList := AMailRefList;
end;

procedure TMailMulitRefStorageCommand.DoLoad(AStorage: TMailStorage);
var
  lDataSet: TDataSet;
  lMailRef: TMailReference;
begin
  lDataSet := AStorage.DataSets[GetDataSetIndex];
  lDataSet.First;
  while not lDataSet.Eof do
  begin
    lMailRef := TMailReference.Create;
    try
      SetMailRef(lDataSet, lMailRef);
      FMailRefList.AddObject(lMailRef.MailGUID, lMailRef);
      lDataSet.Next;
    except
      lMailRef.Free;
      raise;
    end;
  end;
end;

procedure TMailMulitRefStorageCommand.DoSave(AStorage: TMailStorage);
begin
  Assert(False, '不支持的动作');
end;

{ TMailPathStorageCommand }

constructor TMailPathStorageCommand.Create(AMailPath: TMailPath);
begin
  inherited Create;
  FMailPath := AMailPath;
end;

procedure TMailPathStorageCommand.SetPath(ADataSet: TDataSet; AMailPath: TMailPath);
begin
  AMailPath.PathGUID := ADataSet.FieldByName('FGUID').AsString;
  AMailPath.ParentGUID := ADataSet.FieldByName('FPARENTGUID').AsString;
  AMailPath.OwnerGUID := ADataSet.FieldByName('FOWNERGUID').AsString;
  AMailPath.DisplayName := ADataSet.FieldByName('FDISPLAYNAME').AsString;
  AMailPath.ParentPath := ADataSet.FieldByName('FPATH').AsString;
end;

procedure TMailPathStorageCommand.DoLoad(AStorage: TMailStorage);
var
  lDataSet: TDataSet;
begin
  if Key.ScopeKind = TMailScopeKind.skGUID then
  begin
    lDataSet := AStorage.DataSets[GetDataSetIndex];
    if lDataSet.Locate('FGUID', Key.Scope, []) then
      SetPath(lDataSet, FMailPath);
  end
  else
    Assert(False, '没有支持的方法');
end;

procedure TMailPathStorageCommand.DoSave(AStorage: TMailStorage);
var
  lDataSet: TDataSet;
begin
  if Key.ScopeKind = TMailScopeKind.skGUID then
  begin
    lDataSet := AStorage.DataSets[GetDataSetIndex];
    if lDataSet.Locate('FGUID', Key.Scope, []) then
      lDataSet.Edit
    else
    begin
      lDataSet.Append;
      lDataSet.FieldByName('FCLIENTACCOUNT').AsString := BizSys.BizSystem.ClientAccount;
    end;

    lDataSet.FieldByName('FGUID').AsString := FMailPath.PathGUID;
    lDataSet.FieldByName('FPARENTGUID').AsString := FMailPath.ParentGUID;
    lDataSet.FieldByName('FOWNERGUID').AsString := FMailPath.OwnerGUID;
    lDataSet.FieldByName('FDISPLAYNAME').AsString := FMailPath.DisplayName;
    lDataSet.FieldByName('FPATH').AsString := FMailPath.ParentPath;

    lDataSet.Post;
  end
  else
    Assert(False, '没有支持的方法');
end;

function TMailPathStorageCommand.GetDataSetIndex: Integer;
begin
  Result := MPS;
end;

{ TMailMulitPathStorageCommand }

constructor TMailMulitPathStorageCommand.Create(AMailPathList: TStrings);
begin
  inherited Create;
  FMailPathList := AMailPathList;
end;

procedure TMailMulitPathStorageCommand.DoLoad(AStorage: TMailStorage);
var
  lDataSet: TDataSet;
  lMailPath: TMailPath;
begin
  lDataSet := AStorage.DataSets[GetDataSetIndex];
  lDataSet.First;
  while not lDataSet.Eof do
  begin
    lMailPath := TMailPath.Create;
    try
      SetPath(lDataSet, lMailPath);
      FMailPathList.AddObject(lMailPath.FullPath, lMailPath);
      lDataSet.Next;
    except
      lMailPath.Free;
      raise;
    end;
  end;
end;

procedure TMailMulitPathStorageCommand.DoSave(AStorage: TMailStorage);
begin
  Assert(False, '不支持的动作');
end;

{ TMailInfo }

constructor TMailInfo.Create;
begin
  inherited;

  MailGUID := '';
  UIDL := '';
  HasAttach := False;

  Tos := '';
  From := '';
  Cc := '';
  Bcc := '';
  ReplyTo := '';
  Subject := '';
  HeaderText := '';

  FAttachmentInfos := TStringList.Create;
end;

destructor TMailInfo.Destroy;
begin
  FAttachmentInfos.Free;
  inherited;
end;

procedure TMailInfo.Assign(Source: TPersistent);
var
  lSourceMailInfo: TMailInfo;
begin
  lSourceMailInfo := TMailInfo(Source);

  MailGUID    := lSourceMailInfo.MailGUID;
  UIDL        := lSourceMailInfo.UIDL;
  Size        := lSourceMailInfo.Size;
  HasAttach   := lSourceMailInfo.HasAttach;

  Tos        := lSourceMailInfo.Tos;
  From       := lSourceMailInfo.From;
  Cc         := lSourceMailInfo.Cc;
  Bcc        := lSourceMailInfo.Bcc;
  ReplyTo    := lSourceMailInfo.ReplyTo;
  Subject    := lSourceMailInfo.Subject;
  Priority   := lSourceMailInfo.Priority;
  Date       := lSourceMailInfo.Date;
  HeaderText := lSourceMailInfo.HeaderText;
  AttachmentInfos.Text := lSourceMailInfo.AttachmentInfos.Text;
end;

function TMailInfo.GetHeaderText: string;
begin
  Result := FHeaderText;
end;

procedure TMailInfo.SetHeaderText(const Value: string);
begin
   FHeaderText := Value;
  UpdateProperties;
end;

procedure TMailInfo.UpdateProperties;
begin
  //TODO: 解析Header,生成属性
  //是否在设置属性后还要修改HeaderText呢?
end;

{ TMailSystem }

constructor TMailSystem.Create;
begin
  inherited;

  FMailInfoList := TStringList.Create;
  FMailInfoList.Sorted := True;
  FMailInfoList.Duplicates := TDuplicates.dupError;

  FMailRefList := TStringList.Create;
  FMailRefList.Sorted := True;
  FMailRefList.Duplicates := TDuplicates.dupError;

  FReceiveMsgExchg := TMessageExchanger.Create(nil);
  FReceiveMsgExchg.MessageKind := TMessageKind.mkReceive;
  FSendMsgExchg := TMessageExchanger.Create(nil);
  FSendMsgExchg.MessageKind := TMessageKind.mkSend;
  FOutMails := TStringList.Create;
  FMailSending := TStringList.Create;
end;

destructor TMailSystem.Destroy;
var
  I: Integer;
  lObject: TObject;
begin
  for I := 0 to FMailInfoList.Count - 1 do
  begin
    lObject := FMailInfoList.Objects[I];
    FreeAndNil(lObject);
  end;
  FMailInfoList.Free;

  for I := 0 to FMailRefList.Count - 1 do
  begin
    lObject := FMailRefList.Objects[I];
    FreeAndNil(lObject);
  end;
  FMailRefList.Free;

  FReceiveMsgExchg.Free;
  FSendMsgExchg.Free;
  FOutMails.Free;
  FMailSending.Free;

  inherited;
end;

static function TMailSystem.CreateMailSystem: TMailSystem;
begin
  if MailSystem = nil then
    MailSystem := TMailSystem.Create;
  Result := MailSystem;
end;

static procedure TMailSystem.DestroyMailSystem;
begin
  if MailSystem <> nil then
  begin
    MailSystem.Free;
    MailSystem := nil;
  end;
end;

function TMailSystem.FindMailInfo(const AMailGUID: string): TMailInfo;
var
  I: Integer;
  lMailStorage: TMailStorage;
  lMailInfo: TMailInfo;
begin
  Result := DoFindMailInfo(AMailGUID);
  if Result = nil then
  begin
    lMailInfo := TMailInfo.Create;
    try
      lMailStorage := TMailStorage.Create(TMailStorageAction.msaLoad);
      try
        lMailStorage.AddMailInfoCommand(MakeKey(AMailGUID, TMailScopeKind.skGUID, '', ''), lMailInfo);
        lMailStorage.Execute;
        if lMailInfo.MailGUID <> '' then
        begin
          AddMailInfo(lMailInfo);
          Result := lMailInfo;
        end;
      finally
        lMailStorage.Free;
      end;
    except
      lMailInfo.Free;
      raise;
    end;
  end;
end;

function TMailSystem.FindMailRef(const AMailGUID, APath: string): TMailReference;
var
  lMailStorage: TMailStorage;
  lMailRef: TMailReference;
begin
  Result := DoFindMailRef(AMailGUID, APath);

  if Result = nil then
  begin
    lMailRef := TMailReference.Create;
    try
      lMailStorage := TMailStorage.Create(TMailStorageAction.msaLoad);
      try
        lMailStorage.AddMailRefCommand(MakeKey(AMailGUID, TMailScopeKind.skGUID, OperateUser, APath), lMailRef);
        lMailStorage.Execute;
        if lMailRef.MailGUID <> '' then
        begin
          Assert(SameText(lMailRef.OwnerGUID, OperateUser), '');
          AddMailRef(lMailRef);
          Result := lMailRef;
        end;
      finally
        lMailStorage.Free;
      end;
    except
      lMailRef.Free;
      raise;
    end;
  end;
end;

function TMailSystem.GetMailInfo(const AMailGUID: string): TMailInfo;//包含附件列表
begin
  Result := FindMailInfo(AMailGUID);
  if Result = nil then
    raise Exception.CreateFmt('没有找到 GUID = "%s" 的邮件', [AMailGUID]);
end;

function TMailSystem.GetMailRef(const AMailGUID, APath: string): TMailReference;
begin
  Result := FindMailRef(AMailGUID, APath);
  if Result = nil then
    raise Exception.CreateFmt('没有在目录 "%s" 下面找到 GUID = "%s" 的邮件', [APath, AMailGUID]);
end;

function TMailSystem.SaveMail(const AMailGUID, APath: string;
  AMailStream: TStream): TMailReference;
var
  lMailRef: TMailReference;
  lMailContent: TStream;
  lMailAttachments: TList;
begin
  //TODO: 分解MailStream,然后创建邮件
  SaveMail(lMailRef, lMailContent, lMailAttachments);
end;

procedure TMailSystem.SaveMail(AMailRef: TMailReference; AMailContent: TStream;
  AMailAttachments: TList);
var
  lMailStorage: TMailStorage;
  lMailAttachment: TMailAttachment;
begin
  lMailStorage := TMailStorage.Create(TMailStorageAction.msaSave);
  lMailAttachment := TMailAttachment.Create;
  try
    lMailStorage.AddMailInfoCommand(MakeKey(AMailRef.MailGUID,
      TMailScopeKind.skGUID, '', ''), AMailRef.MailInfo);
    lMailStorage.AddMailRefCommand(MakeKey(AMailRef.MailGUID,
      TMailScopeKind.skGUID, OperateUser, AMailRef.PathGUID), AMailRef);

    if AMailContent <> nil then
    begin
      if AMailContent.Size > 0 then
        lMailAttachment.Content.CopyFrom(AMailContent, 0);
      lMailAttachment.Index := 0;
      lMailStorage.AddStreamCommand(MakeKey(AMailRef.MailGUID,
        TMailScopeKind.skGUID, '', ''), lMailAttachment);
    end;
    if AMailAttachments <> nil then
      lMailStorage.AddMultiAttachmentCommand(MakeKey(AMailRef.MailGUID,
        TMailScopeKind.skGUID, '', ''), AMailAttachments, 1);
    lMailStorage.Execute;

    AddMailRef(AMailRef);
  finally
    lMailAttachment.Free;
    lMailStorage.Free;
  end;
end;

procedure TMailSystem.DeleteMail(const AMailGUID, APath: string);
var
  lMailStorage: TMailStorage;
  lMailRef: TMailReference;
begin
  lMailStorage := TMailStorage.Create(TMailStorageAction.msaDelete);
  lMailRef := TMailReference.Create;
  try
    lMailRef.MailGUID := AMailGUID;
    lMailRef.PathGUID := APath;
    lMailStorage.AddMailRefCommand(MakeKey(AMailGUID, TMailScopeKind.skGUID,
      OperateUser, APath), lMailRef);
    lMailStorage.Execute;

    DeleteMailInfo(AMailGUID);
    DeleteMailRef(AMailGUID, APath);
  finally
    lMailRef.Free;
    lMailStorage.Free;
  end;
end;

procedure TMailSystem.MoveMail(const AMailGUID, AOldPath,
  ANewPath: string);
var
  lMailStorage: TMailStorage;
  lMailRef: TMailReference;
begin
  lMailStorage := TMailStorage.Create(TMailStorageAction.msaMove);
  try
    lMailStorage.AddMoveRefCommand(MakeKey(AMailGUID, TMailScopeKind.skGUID,
      OperateUser, AOldPath), ANewPath);
    lMailStorage.Execute;

    DeleteMailRef(AMailGUID, AOldPath);
    {lMailRef := DoFindMailRef(AMailGUID, ANewPath);
    if lMailRef <> nil then
      lMailRef.PathGUID := ANewPath;
      //raise Exception.Create('移动邮件出错');}
  finally
    lMailStorage.Free;
  end;
end;

procedure TMailSystem.SaveMailRef(AMailRef: TMailReference);
var
  lMailStorage: TMailStorage;
begin
  lMailStorage := TMailStorage.Create(TMailStorageAction.msaSave);
  try
    lMailStorage.AddMailRefCommand(MakeKey(AMailRef.MailGUID,
      TMailScopeKind.skGUID, AMailRef.OwnerGUID, AMailRef.PathGUID), AMailRef);
    lMailStorage.Execute;

    AddMailRef(AMailRef);
  finally
    lMailStorage.Free;
  end;
end;

procedure TMailSystem.SaveMailRefList(AMailRefList: TObjectList);
var
  lMailStorage: TMailStorage;
  I: Integer;
  lMailRef: TMailReference;
begin
  lMailStorage := TMailStorage.Create(TMailStorageAction.msaSave);
  try
    for I := 0 to AMailRefList.Count - 1 do
    begin
      lMailRef := TMailReference(AMailRefList.Items[I]);
      lMailStorage.AddMailRefCommand(MakeKey(lMailRef.MailGUID,
        TMailScopeKind.skGUID, lMailRef.OwnerGUID, lMailRef.PathGUID), lMailRef);
    end;
    lMailStorage.Execute;
  finally
    lMailStorage.Free;
  end;
end;

procedure TMailSystem.LoadMailContent(const AMailGUID: string; AStream: TStream);
var
  lMailAttachment: TMailAttachment;
begin
  lMailAttachment := TMailAttachment.Create;
  try
    lMailAttachment.Index := 0;
    LoadMailAttachment(AMailGUID, lMailAttachment);
    AStream.CopyFrom(lMailAttachment.Content, 0);
  finally
    lMailAttachment.Free;
  end;
end;

procedure TMailSystem.LoadMailAttachment(const AMailGUID: string;
  AMailAttachment: TMailAttachment);
var
  lMailStorage: TMailStorage;
begin
  lMailStorage := TMailStorage.Create(TMailStorageAction.msaLoad);
  try
    lMailStorage.AddStreamCommand(MakeKey(AMailGUID, TMailScopeKind.skGUID, '', ''), AMailAttachment);
    lMailStorage.Execute;
  finally
    lMailStorage.Free;
  end;
end;

procedure TMailSystem.LoadMailAttachment(const AMailGUID: string; Index: Integer; AStream: TStream);
var
  lMailAttachment: TMailAttachment;
begin
  lMailAttachment := TMailAttachment.Create;
  try
    lMailAttachment.Index := Index;
    LoadMailAttachment(AMailGUID, lMailAttachment);
    AStream.Size := lMailAttachment.Content.Size;
    AStream.CopyFrom(lMailAttachment.Content, 0);
  finally
    lMailAttachment.Free;
  end;
end;

function TMailSystem.DoSearchMail(const AOperateUser, APath: string;
  const ASearchMailParam: TSearchMailParam; AMailRefList: TStrings): Integer;
var
  lMailStorage: TMailStorage;
  lMailRefList: TStringList;
  I: Integer;
  lMailRef: TMailReference;
begin
  lMailRefList := TStringList.Create;
  try
    lMailStorage := TMailStorage.Create(TMailStorageAction.msaLoad);
    try
      lMailStorage.AddMailMulitRefCommand(MakeKey(APath, TMailScopeKind.skPath, AOperateUser, ''),
        ASearchMailParam, lMailRefList);
      lMailStorage.Execute;
    finally
      lMailStorage.Free;
    end;

    Result := lMailRefList.Count;

    //加入缓存
    for I := 0 to lMailRefList.Count - 1 do
    begin
      lMailRef := TMailReference(lMailRefList.Objects[I]);
      if SameText(lMailRef.OwnerGUID, OperateUser) then
      begin
        AddMailRef(lMailRef);
        lMailRefList.Objects[I] := lMailRef;
      end;
    end;

    if AMailRefList <> nil then
      AMailRefList.AddStrings(lMailRefList);
  finally
    lMailRefList.Free;
  end;
end;

function TMailSystem.SearchMail(const APath: string;
  const ASearchMailParam: TSearchMailParam; AMailRefList: TStrings): Integer;
begin
  Result := DoSearchMail(OperateUser, APath, ASearchMailParam, AMailRefList);
end;

function TMailSystem.SearcbMailUIDL(const AUIDL, AOperateUser: string;
  AMailRefList: TStrings): Boolean;
begin
  //把UIDL赋值给lSearchMailParam,并且从全局中查找
  Result := DoSearchMail(AOperateUser, '\',
    SearchMailParam('', '', '', '', AUIDL, 0, 0, -1, True), AMailRefList) > 0;
end;

function TMailSystem.SearchMailCount(const AMailGUID, AOperateUser: string): Integer;
begin
  Result := DoSearchMail(AOperateUser, '\', SearchMailParam(AMailGUID, True), nil);
end;

procedure TMailSystem.GetMailPathList(APathList: TStrings);
var
  lMailStorage: TMailStorage;
begin
  lMailStorage := TMailStorage.Create(TMailStorageAction.msaLoad);
  try
    lMailStorage.AddMulitPathCommand(
      MakeKey('', TMailScopeKind.skPath, OperateUser, ''), APathList);
    lMailStorage.Execute;
  finally
    lMailStorage.Free;
  end;
end;

procedure TMailSystem.GetMailPath(const APathGUID: string; AMailPath: TMailPath);
var
  lMailStorage: TMailStorage;
begin
  if GetDefaultMailPath(APathGUID, AMailPath) then
    Exit;

  lMailStorage := TMailStorage.Create(TMailStorageAction.msaLoad);
  try
    lMailStorage.AddPathCommand(MakeKey(APathGUID, TMailScopeKind.skGUID, OperateUser, ''), AMailPath);
    lMailStorage.Execute;
    if AMailPath.PathGUID = '' then
      raise Exception.CreateFmt('GUID = "%s% 的目录不存在', [APathGUID]);
  finally
    lMailStorage.Free;
  end;
end;

function TMailSystem.AddMailPath(const AGUID, AParentPathGUID,
  APathDisplayname: string): TMailPath;
var
  lMailStorage: TMailStorage;
  lMailPath, lMailParentPath: TMailPath;
begin
  lMailStorage := TMailStorage.Create(TMailStorageAction.msaSave);
  try
    lMailPath := TMailPath.Create;
    try
      //如果AGUID不为空,说明要修改这个目录的一些属性
      if AGUID = '' then
        lMailPath.PathGUID := CreateGUIDStr
      else
        lMailPath.PathGUID := AGUID;
      lMailPath.ParentGUID := AParentPathGUID;
      lMailPath.OwnerGUID := OperateUser;
      lMailPath.DisplayName := APathDisplayName;
      lMailParentPath := TMailPath.Create;
      try
        GetMailPath(AParentPathGUID, lMailParentPath);
        lMailPath.ParentPath := lMailParentPath.FullPath;
      finally
        lMailParentPath.Free;
      end;

      lMailStorage.AddPathCommand(MakeKey(lMailPath.PathGUID,
        TMailScopeKind.skGUID, OperateUser, ''), lMailPath);
      lMailStorage.Execute;
      Result := lMailPath;
    except
      lMailPath.Free;
      raise;
    end;
  finally
    lMailStorage.Free;
  end;
end;

procedure TMailSystem.DeletePath(const APathGUID: string);
var
  lMailStorage: TMailStorage;
begin
  lMailStorage := TMailStorage.Create(TMailStorageAction.msaDelete);
  try
    lMailStorage.AddPathCommand(MakeKey(APathGUID, TMailScopeKind.skGUID,
      OperateUser, ''), nil);
    lMailStorage.Execute;
  finally
    lMailStorage.Free;
  end;
end;

procedure TMailSystem.MovePath(const APathGUID, AOldParentPathGUID,
  ANewParentPathGUID: string);
var
  lMailStorage: TMailStorage;
  lMailPath, lNewMailPath: TMailPath;
  lNewPath: string;
begin
  lMailStorage := TMailStorage.Create(TMailStorageAction.msaMove);
  lMailPath := TMailPath.Create;
  lNewMailPath := TMailPath.Create;
  try
    GetMailPath(APathGUID, lMailPath);
    GetMailPath(ANewParentPathGUID, lNewMailPath);
    lNewPath := lMailPath.ParentPath;
    {if AOldParentPathGUID = cGUID_Root then
      lNewPath := lNewPath + ANewParentPathGUID + '\'
    else
      lNewPath := Copy(lNewPath, 1, Length(lNewPath) - Length(lMailPath.ParentPath) - 1) +//AOldParentPathGUID
        ANewParentPathGUID + '\';}
    lNewPath := lNewMailPath.FullPath;
    lMailStorage.AddMovePathCommand(MakeKey(APathGUID, TMailScopeKind.skGUID, OperateUser, ''),
      AOldParentPathGUID, ANewParentPathGUID, lMailPath.ParentPath, lNewPath);
    lMailStorage.Execute;
  finally
    lMailStorage.Free;
    lMailPath.Free;
    lNewMailPath.Free;
  end;
end;

function TMailSystem.DoGetAccountList(const AOperateUser: string; AMailAccountList: TStrings): Integer;
var
  lMailStorage: TMailStorage;
begin
  lMailStorage := TMailStorage.Create(TMailStorageAction.msaLoad);
  try
    lMailStorage.AddMulitAccountCommand(
      MakeKey('', TMailScopeKind.skPath, OperateUser, ''), AMailAccountList);
    lMailStorage.Execute;
    Result := AMailAccountList.Count;
  finally
    lMailStorage.Free;
  end;
end;

procedure TMailSystem.CheckAccounts;
var
  I: Integer;
  lStrings: TStringList;
begin
  if FAccounts = nil then
  begin
    lStrings := TStringList.Create;
    try
      lStrings.Sorted := True;
      DoGetAccountList(OperateUser, lStrings);
      FAccounts := TMailAccounts.Create(True);
      for I := 0 to lStrings.Count - 1 do
        FAccounts.Add(TMailAccount(lStrings.Objects[I]));
    finally
      lStrings.Free;
    end;
  end;
end;

function TMailSystem.GetDefaultMailPath(const APathGUID: string; AMailPath: TMailPath): Boolean;
var
  lPathGUID: string;
  lDisplayName: string;
begin
  Result := True;
  lPathGUID := UpperCase(APathGUID);
  if SameText(lPathGUID, cGUID_Root) then
    lDisplayName := cRootText
  else if SameText(lPathGUID, cGUID_InBox) then
    lDisplayName := cInboxText
  else if SameText(lPathGUID, cGUID_OutBox) then
    lDisplayName := cOutboxText
  else if SameText(lPathGUID, cGUID_Draft) then
    lDisplayName := cDraftText
  else if SameText(lPathGUID, cGUID_SentBox) then
    lDisplayName := cSentBoxText
  else if SameText(lPathGUID, cGUID_DelBox) then
    lDisplayName := cDelBoxText
  else
    Result := False;

  if Result then
  begin
    AMailPath.PathGUID := lPathGUID;
    AMailPath.ParentGUID := cGUID_Root;
    AMailPath.OwnerGUID := OperateUser;
    AMailPath.DisplayName := lDisplayName;
    if SameText(lPathGUID, cGUID_Root) then
      AMailPath.ParentPath := ''
    else
      AMailPath.ParentPath := '\';
    AMailPath.FReadOnly := True;
  end;
end;

procedure TMailSystem.AddAccount(AMailAccount: TMailAccount);
var
  lMailStorage: TMailStorage;
  I: Integer;
  lMailAccount: TMailAccount;
begin
  lMailStorage := TMailStorage.Create(TMailStorageAction.msaSave);
  lMailAccount := TMailAccount.create; //创建备用的MailAccount;
  lMailAccount.Assign(AMailAccount);
  try
    Assert(AMailAccount.OwnerID = OperateUser, '不能够操作其他用户的帐号');
    lMailStorage.AddAccountCommand(MakeKey(AMailAccount.AccountGUID,
      TMailScopeKind.skGUID, OperateUser, ''), AMailAccount);
    lMailStorage.Execute;
    CheckAccounts;
    I := FAccounts.IndexOf(AMailAccount);
    if I = -1 then
      FAccounts.Add(lMailAccount)
    else
      if FAccounts[I] <> AMailAccount then
      begin
        FAccounts.Delete(I);  //该操作会释放掉AMailAccount;
        FAccounts.Insert(I, lMailAccount);
      end;
  finally
    AMailAccount.Assign(lMailAccount);
    lMailStorage.Free;
  end;
end;

procedure TMailSystem.SaveAccount(AMailAccount: TMailAccount);
begin
  AddAccount(AMailAccount);
end;

procedure TMailSystem.DeleteAccount(AMailAccount: TMailAccount);
var
  lMailStorage: TMailStorage;
begin
  lMailStorage := TMailStorage.Create(TMailStorageAction.msaDelete);
  try
    Assert(AMailAccount.OwnerID = OperateUser, '不能够操作其他用户的帐号');
    lMailStorage.AddAccountCommand(MakeKey(AMailAccount.AccountGUID,
      TMailScopeKind.skGUID, OperateUser, ''), AMailAccount);
    lMailStorage.Execute;
    CheckAccounts;
    FAccounts.Remove(AMailAccount);
  finally
    lMailStorage.Free;
  end;
end;

function TMailSystem.AccountCount: Integer;
begin
  CheckAccounts;
  Result := FAccounts.Count;
end;

function TMailSystem.FindAccountByGUID(const AccountGUID: string): TMailAccount;
begin
  CheckAccounts;
  Result := FAccounts.FindAccountByGUID(AccountGUID);
end;

function TMailSystem.FindAccountByAddress(const MailAddress: string): TMailAccount;
begin
  CheckAccounts;
  Result := FAccounts.FindAccountByAddress(MailAddress);
end;

function TMailSystem.FindAccountByID(const AccountID: string): TMailAccount;
begin
  CheckAccounts;
  Result := FAccounts.FindAccountByID(AccountID);
end;

function TMailSystem.GetAccounts(const Index: Integer): TMailAccount;
begin
  CheckAccounts;
  Result := FAccounts.Items[Index];
end;

function TMailSystem.GetOperUser: string;
begin
  Result := TSystemCore.Operator.ID;
end;

function TMailSystem.GetOperUserDisplayName: string;
begin
  Result := TSystemCore.Operator.DisplayName;
end;

function TMailSystem.MakeKey(const AScope: string;
  const AScopeKind: TMailScopeKind; const AOwnerGUID, APathGUID: string): TMailKey;
begin
  Result.Scope := AScope;
  Result.ScopeKind := AScopeKind;
  Result.OwnerGUID := AOwnerGUID;
  Result.PathGUID := APathGUID;
end;

function TMailSystem.DoFindMailInfo(const AMailGUID: string): TMailInfo;
var
  I: Integer;
begin
  if FMailInfoList.Find(AMailGUID, I) then
    Result := TMailInfo(FMailInfoList.Objects[I])
  else
    Result := nil;
end;

function TMailSystem.DoFindMailRef(const AMailGUID,
  APath: string): TMailReference;
var
  I: Integer;
begin
  if FMailRefList.Find(APath + '*' + AMailGUID, I) then
    Result := TMailReference(FMailRefList.Objects[I])
  else
    Result := nil;
end;

procedure TMailSystem.DoSentMail(const AMailGUID: string);
begin
  if Assigned(OnSentMail) then
    OnSentMail(Self, AMailGUID);
end;

procedure TMailSystem.DoReceiveNewMail(const AMailGUID: string);
begin
  if Assigned(OnReceiveNewMail) then
    OnReceiveNewMail(Self, AMailGUID);
end;

procedure TMailSystem.AddMailInfo(AMailInfo: TMailInfo);
var
  I: Integer;
  lObject: TObject;
begin
  if FMailInfoList.Find(AMailInfo.MailGUID, I) then
  begin
    lObject := FMailInfoList.Objects[I];
    FMailInfoList.Objects[I] := AMailInfo;
    FreeAndNil(lObject);
  end
  else
    FMailInfoList.AddObject(AMailInfo.MailGUID, AMailInfo);
end;

procedure TMailSystem.AddMailRef(var AMailRef: TMailReference);
var
  I: Integer;
  lObject: TObject;
begin
  if FMailRefList.Find(AMailRef.PathGUID + '*' + AMailRef.MailGUID, I) then
  begin
    if FMailRefList.Objects[I] <> AMailRef then
    begin
      TMailReference(FMailRefList.Objects[I]).Assign(AMailRef);
      //lObject := AMailRef;
      //AMailRef := TMailReference(FMailRefList.Objects[I]);
      //FreeAndNil(lObject);
    end;
  end
  else
    FMailRefList.AddObject(AMailRef.PathGUID + '*' + AMailRef.MailGUID, AMailRef);
  DeleteMailInfo(AMailRef.MailGUID);
end;

procedure TMailSystem.DeleteMailInfo(const AMailGUID: string);
var
  I: Integer;
  lObject: TObject;
begin
  if FMailInfoList.Find(AMailGUID, I) then
  begin
    lObject := FMailInfoList.Objects[I];
    FreeAndNil(lObject);
    FMailInfoList.Delete(I);
  end;
end;

procedure TMailSystem.DeleteMailRef(const AMailGUID, APath: string);
var
  I: Integer;
  lObject: TObject;
begin
  if FMailRefList.Find(APath + '*' + AMailGUID, I) then
  begin
    lObject := FMailRefList.Objects[I];
    FreeAndNil(lObject);
    FMailRefList.Delete(I);
  end;
end;

procedure TMailSystem.MsgStreamToMail(AMailRef: TMailReference; AMailContent: TStream;
  AMailAttachments: TList; AMsgStream: TStream);
var
  lMsg: TMessage;
  lM2M: TMessage2Mail;
begin
  lMsg := TMessage.Create(nil);
  try
    lM2M := TMessage2Mail.Create;
    try
      lMsg.LoadFromStream(AMsgStream);
      lM2M.MessageToMail(lMsg, AMailRef.MailInfo, AMailcontent, AMailAttachments);

      AMailRef.OwnerGUID := OperateUser;
      AMailRef.MailInfo.MailGUID := AMailRef.MailGUID;
      AMailRef.Size := AMsgStream.Size;
      AMailRef.MailInfo.Size := AMailRef.Size;

      if Assigned(FindAccountByAddress(AMailRef.MailInfo.From)) then
        AMailRef.AccountGUID := FindAccountByAddress(AMailRef.MailInfo.From).AccountGUID;
    finally
      lM2M.Free;
    end;
  finally
    lMsg.Free;
  end;
end;

procedure TMailSystem.MailToMsgStream(AMailInfo: TMailInfo;
  AMailContent: TStream; AMailAttachments: TList; AMsgStream: TStream);
var
  I: Integer;
  lMsg: TMessage;
  lM2M: TMessage2Mail;
  lAttachStream: TStream;
  lMailAttachment: TMailAttachment;
begin
  if AMailContent.Size = 0 then
    LoadMailContent(AMailInfo.MailGUID, AMailContent);

  for I := 0 to AMailInfo.AttachmentInfos.Count - 1 do
  begin
    lAttachStream := THugeMemoryStream.Create(0);
    try
{  这里不加载附件,使用时在记载,结构不好    lMailAttachment := TMailAttachment.Create;
      try
        //必须加1,因为0号附件是邮件体
        lMailAttachment.Index := I + 1;
        LoadMailAttachment(AMailInfo.MailGUID, lMailAttachment);
        if lMailAttachment.Content.Size > 0 then
          lAttachStream.CopyFrom(lMailAttachment.Content, 0);
      finally
        lMailAttachment.Free;
      end;}
      AMailAttachments.Add(lAttachStream);
    except
      lAttachStream.Free;
      raise;
    end;
  end;

  lM2M := TMessage2Mail.Create;
  try
    lMsg := TMessage.Create(nil);
    try
      AMailContent.Position := 0;
      lM2M.MailToMessage(AMailInfo, AMailContent, AMailAttachments, lMsg);
      lMsg.SaveToStream(AMsgStream);
    finally
      lMsg.Free;
    end;
  finally
    lM2M.Free;
  end;
end;

static function TMailSystem.UnloadStreamHeader: string;
begin
  Result := '?Unload.Stream?'
end;

procedure TMailSystem.Send;
var
  I: Integer;
  lMailInfo: TMailInfo;
  lStrings: TStrings;
begin
  lStrings := TStringList.Create;
  try
    for I := 0 to OutMails.Count - 1 do
    begin
      lMailInfo := FindMailInfo(OutMails[I]);
      if lMailInfo <> nil then
      begin
        //if (lMailRef.State and msInternal) = msInternal then
        //if (lMailRef.State and msInternal) <> msInternal then//SendExternalMail
        lStrings.Add(OutMails[I]);
      end;
    end;
    Send(lStrings);
  finally
    lStrings.Free;
  end;
end;

procedure TMailSystem.Send(MailList: TStrings);
var
  I: Integer;
begin
  if SendMsgExchg.Sending or ReceiveMsgExchg.Receiving then
    exit;
  MailSending.Clear;
  SendMsgExchg.SMTPMessages.Clear;
  for I := 0 to MailList.Count - 1 do
    Send(MailList[I], MailList.Count, I + 1);
end;

procedure TMailSystem.Send(const AMailGUID: string; const AMailCount, AMailIndex: Integer);
var
  ExternalSenders, InternalSenders: String;
  I: Integer;
  lMailRef: TMailReference;
  lMailInfo: TMailInfo;
begin
  //关于邮件发送的说明
  //1、首先要解析接收者,分拣为内部用户和外部用户
  //2、发送内部邮件(假定不会出现发送失败)
  //3、发送外部邮件,将其交送给内部的Exchange,发送结束后,无论是否成功都显示发送成功
  //4、如果外部邮件发送失败,就将发送消息和失败的用户信息等组装成一封发送错误的邮件返回给用户

  lMailInfo := FindMailInfo(AMailGUID);
  if lMailInfo <> nil then
  begin
    TIBMailConst.SplitSenders(lMailInfo.Tos, ExternalSenders, InternalSenders);
    TIBMailConst.SplitSenders(lMailInfo.Cc, ExternalSenders, InternalSenders);
    TIBMailConst.SplitSenders(lMailInfo.Bcc, ExternalSenders, InternalSenders);

    if InternalSenders <> '' then
      SendInternalMail(AMailGUID, InternalSenders, AMailCount, AMailIndex);
    if ExternalSenders <> '' then
      SendExternalMail(AMailGUID, ExternalSenders);

    if ExternalSenders = '' then
    begin
      I := OutMails.IndexOf(AMailGUID);
      if I <> -1 then
        OutMails.Delete(I);
      DoSentMail(AMailGUID);
    end;
  end;
end;

procedure TMailSystem.StartReceiveMail(AccountGUIDs: TStrings);
var
  I: Integer;
  lAccount: TMailAccount;
begin
  if SendMsgExchg.Sending or ReceiveMsgExchg.Receiving then
    Exit;

  ReceiveInternalMail;

  ReceiveMsgExchg.POPAccounts.Clear;
  for I := 0 to AccountGUIDs.Count - 1 do
  begin
    lAccount := FindAccountByGUID(AccountGUIDs.Strings[I]);
    if Assigned(lAccount) then
      ReceiveMsgExchg.POPAccounts.Add(lAccount.POPHost, lAccount.UserID,
        lAccount.Password, lAccount.POPPort, lAccount.DeleteOnRetrieve);
  end;
  //ReceiveMsgExchg.Active := True;
end;

procedure TMailSystem.SendExternalMail(const AMailGUID, ASenders: string);
var
  lAccount: TMailAccount;
  lStream: TStream;
  lMailContent: TStream;
  lMailAttachments: TObjectList;
  lMailInfo: TMailInfo;
begin
  lMailInfo := FindMailInfo(AMailGUID);
  if lMailInfo <> nil then
  begin
    lAccount := FindAccountByAddress(lMailInfo.From);
    if lAccount <> nil then
    begin
      lMailAttachments := TObjectList.Create(True);
      lMailContent := THugeMemoryStream.Create(0);
      lStream := THugeMemoryStream.Create(0);
      try
        MailToMsgStream(lMailInfo, lMailContent, lMailAttachments, lStream);
        lStream.Position := 0;

        if lAccount.Verify then
        begin
          if lAccount.DifferPOP then
            SendMsgExchg.SMTPMessages.Add(lAccount.VerifyAccount,
              lAccount.VerifyPassword, lAccount.SMTPHost,
              lMailInfo.From, ASenders, lStream, lAccount.SMTPPort)
          else
            SendMsgExchg.SMTPMessages.Add(lAccount.UserID, lAccount.Password,
              lAccount.SMTPHost, lMailInfo.From, ASenders, lStream, lAccount.SMTPPort);
        end
        else
          SendMsgExchg.SMTPMessages.Add(lAccount.SMTPHost, lMailInfo.From,
            ASenders, lStream, lAccount.SMTPPort);
      finally
        lStream.Free;
        lMailContent.Free;
        lMailAttachments.Free;
      end;
      MailSending.Add(lMailInfo.MailGUID);
    end;
  end;
end;

procedure TMailSystem.BeforeReceivMail(const AccountGUID, UIDL: String;
  var Receive: Boolean);
var
  lAccount: TMailAccount;
begin
  lAccount := FindAccountByGUID(AccountGUID);
  if Assigned(lAccount) then
    Receive := not SearcbMailUIDL(UIDL, OperateUser, nil)
  else
    Receive := False;
end;

procedure DoSetNewMailRefProperties(AMailRef: TMailReference; AAccount: TMailAccount);
begin
  AMailRef.OwnerGUID := TMailLibrary.GetOwnerID;
  AMailRef.PathGUID := cGUID_InBox;
  AMailRef.Time := SysUtils.Now;
  AMailRef.State := msReceived;
  if AAccount <> nil then
    AMailRef.AccountGUID := AAccount.AccountGUID;
end;

procedure TMailSystem.ReceiveMail(const AccountGUID: String;
  AStream: TStream; const UIDL: String; var Delete: Boolean);
var
  lMsg: TMessage;
  lM2M: TMessage2Mail;
  lMailRef: TMailReference;
  lMailContent: TStream;
  lMailAttachments: TList;
  lAccount: TMailAccount;
  lMailRefList: TStringList;
begin
  lAccount := FindAccountByGUID(AccountGUID);
  lMailRef := TMailReference.Create;
  try
    lMsg := TMessage.Create(nil);
    lMailRefList := TStringList.Create;
    try
      if SearcbMailUIDL(UIDL, '', lMailRefList) then
      begin
        lMailRef.Assign(TMailReference(lMailRefList.Objects[0]));
        DoSetNewMailRefProperties(lMailRef, lAccount);
        //保存邮件引用
        SaveMailRef(lMailRef);
      end
      else
      begin
        lM2M := TMessage2Mail.Create;
        lMailContent := THugeMemoryStream.Create(0);
        lMailAttachments := TObjectList.Create(True);
        try
          lMailRef.MailGUID := CreateGUIDStr;
          lMailRef.MailInfo.MailGUID := lMailRef.MailGUID;
          lMailRef.MailInfo.UIDL := UIDL;

          lMsg.LoadFromStream(AStream);
          lM2M.MessageToMail(lMsg, lMailRef.MailInfo, lMailContent, lMailAttachments);
          lMailRef.MailGUID:= lMailRef.MailInfo.MailGUID;
          lMailRef.Size := AStream.Size;
          lMailRef.MailInfo.Size := lMailRef.Size;

          DoSetNewMailRefProperties(lMailRef, lAccount);
          //保存邮件实体和引用
          SaveMail(lMailRef, lMailContent, lMailAttachments);
        finally
          lMailContent.Free;
          lMailAttachments.Free;
          lM2M.Free;
        end;
      end;
      DoReceiveNewMail(lMailRef.MailGUID);
    finally
      lMailRefList.Free;
      lMsg.Free;
    end;
  except
    lMailRef.Free;
    raise;
  end;
end;

procedure TMailSystem.SentMail(MailIndex: Integer);
var
  S: string;
  I: Integer;
  lMailRef: TMailReference;
begin
  if MailIndex >= 0 then   //在发送
  begin
    S := MailSending.Strings[MailIndex];

    {lMailRef := FindMailRef(S, cGUID_OutBox);
    if lMailRef <> nil then
    begin
      lMailRef.State := lMailRef.State or msSent;
      lMailRef.PathGUID := cGUID_SentBox;
      SaveMailRef(lMailRef);
    end;
    }
    I := OutMails.IndexOf(S);
    if I <> -1 then
      OutMails.Delete(I);

    DoSentMail(S);
  end;
end;

procedure TMailSystem.ReceiveInternalMail;
var
  I, J: Integer;
  lMailRef: TMailReference;
  lStrings: TStrings;
  lMailRefList: TObjectList;
begin
  lStrings := TStringList.Create;
  lMailRefList := TObjectList.Create(False);
  try
    if Assigned(LoadFolderMails) then
      LoadFolderMails(Self, lStrings, cGUID_InBox)
    else
      raise Exception.Create('没有提供取指定目录下面邮件的方法');

    for I := 0 to lStrings.Count - 1 do
    begin
      lMailRef := TMailReference(lStrings.Objects[I]);
      J := lMailRef.State;
      if (J and msLoaded) = msLoaded then
      begin
        J := J xor msLoaded;
        lMailRef.State := J or msReceived;
        lMailRefList.Add(lMailRef);
      end;
    end;
    if lMailRefList.Count > 0 then
      SaveMailRefList(lMailRefList);

    for I := 0 to lMailRefList.Count - 1 do
      DoReceiveNewMail(TMailReference(lMailRefList[I]).MailGUID);
  finally
    lStrings.Free;
    lMailRefList.Free;
  end;
end;

procedure TMailSystem.SendInternalMail(const AMailGUID, ASenders: string;
  const AMailCount, AMailIndex: Integer);
var
  I: Integer;
  lMailRefList: TObjectList;
  lMailRef: TMailReference;
  lSenders: TStrings;
  lSize: Integer;
begin
  lSenders := TStringList.Create;
  try
    lSenders.Text := ASenders;
    lMailRefList := TObjectList.Create(True);
    try
      lMailRef := FindMailRef(AMailGUID, cGUID_OutBox);
      lSize := lMailRef.Size;
      for I := 0 to lSenders.Count - 1 do
      begin
        lMailRef := TMailReference.Create;
        lMailRef.OwnerGUID := UpperCase(lSenders[I]);
        lMailRef.MailGUID := AMailGUID;
        lMailRef.PathGUID := cGUID_InBox;
        lMailRef.Time := SysUtils.Now;
        lMailRef.State := msInternal or msLoaded;
        lMailRef.Size := lSize;
        lMailRef.MailInfo.Size := lSize;
        lMailRef.MailInfo.From := 'SMP';

        lMailRefList.Add(lMailRef);
      end;
      SaveMailRefList(lMailRefList);
    finally
      lMailRefList.Free;
    end;
  finally
    lSenders.Free;
  end;
  if Assigned(OnSendInternalMail) then
    OnSendInternalMail(Self, AMailCount, AMailIndex);
end;

procedure TMailSystem.BeforeStartMailExchange;
begin
  if Assigned(OnBeforeStartMailExchange) then
    OnBeforeStartMailExchange(Self);
end;

procedure TMailSystem.StartMailExchange(const AReceive, ASend: Boolean);
begin
  if ASend then
    SendMsgExchg.Active := True;
  if AReceive then
    ReceiveMsgExchg.Active := True;
end;

{ TMailMoveRefStorageCommand }

constructor TMailMoveRefStorageCommand.Create(const ANewPath: string);
begin
  inherited Create;
  FNewPath := ANewPath;
end;

function TMailMoveRefStorageCommand.GetDataSetIndex: Integer;
begin
  Result := -1;
end;

{ TMailMovePathStorageCommand }

constructor TMailMovePathStorageCommand.Create(const AOldParent, ANewParent,
  AOldPath, ANewPath: string);
begin
  inherited Create;
  FOldParent := AOldParent;
  FNewParent := ANewParent;
  FOldPath := AOldPath;
  FNewPath := ANewPath;
end;

function TMailMovePathStorageCommand.GetDataSetIndex: Integer;
begin
  Result := -1;
end;

{ TMailAccountStorageCommand }

function TMailAccountStorageCommand.GetDataSetIndex: Integer;
begin
  Result := MAS;
end;

procedure TMailAccountStorageCommand.SetAccount(ADataSet: TDataSet;
  AMailAccount: TMailAccount);
var
  lStream: TStream;
begin
  AMailAccount.AccountGUID := ADataSet.FieldByName('FGUID').AsString;
  AMailAccount.AccountID := ADataSet.FieldByName('FACCOUNTID').AsString;
  AMailAccount.OwnerID := ADataSet.FieldByName('FOWNERGUID').AsString;
  AMailAccount.MailAddress := ADataSet.FieldByName('FMAILADDRESS').AsString;
  lStream := THugeMemoryStream.Create(0);
  try
    TBlobField(ADataSet.FieldByName('FPROPERTY')).SaveToStream(lStream);
    lStream.Position := 0;
    AMailAccount.LoadPropertiesFromStream(lStream);
  finally
    lStream.Free;
  end;
end;

procedure TMailAccountStorageCommand.DoLoad(AStorage: TMailStorage);
begin
  SetAccount(AStorage.DataSets[GetDataSetIndex], FMailAccount);
end;

procedure TMailAccountStorageCommand.DoSave(AStorage: TMailStorage);
var
  lDataSet: TDataSet;
  lStream: TStream;
begin
  if Key.ScopeKind = TMailScopeKind.skGUID then
  begin
    lDataSet := AStorage.DataSets[GetDataSetIndex];
    if lDataSet.Locate('FGUID', Key.Scope, []) then
      lDataSet.Edit
    else
    begin
      lDataSet.Append;
      lDataSet.FieldByName('FCLIENTACCOUNT').AsString := BizSys.BizSystem.ClientAccount;
    end;

    lDataSet.FieldByName('FGUID').AsString := FMailAccount.AccountGUID;
    lDataSet.FieldByName('FACCOUNTID').AsString := FMailAccount.AccountID;
    lDataSet.FieldByName('FOWNERGUID').AsString := FMailAccount.OwnerID;
    lDataSet.FieldByName('FMAILADDRESS').AsString := FMailAccount.MailAddress;

    lStream := THugeMemoryStream.Create(0);
    try
      FMailAccount.SavePropertiesToStream(lStream);
      TBlobField(lDataSet.FieldByName('FPROPERTY')).LoadFromStream(lStream);
    finally
      lStream.Free;
    end;

    lDataSet.Post;
  end
  else
    Assert(False, '没有支持的方法');
end;

constructor TMailAccountStorageCommand.Create(AMailAccount: TMailAccount);
begin
  inherited Create;
  FMailAccount := AMailAccount;
end;

{ TMailMulitAccountStorageCommand }

procedure TMailMulitAccountStorageCommand.DoLoad(AStorage: TMailStorage);
var
  lDataSet: TDataSet;
  lMailAccount: TMailAccount;
begin
  lDataSet := AStorage.DataSets[GetDataSetIndex];
  lDataSet.First;
  while not lDataSet.Eof do
  begin
    lMailAccount := TMailAccount.Create;
    try
      SetAccount(lDataSet, lMailAccount);
      FMailAccountList.AddObject(lMailAccount.AccountID, lMailAccount);
      lDataSet.Next;
    except
      lMailAccount.Free;
      raise;
    end;
  end;
end;

procedure TMailMulitAccountStorageCommand.DoSave(AStorage: TMailStorage);
begin
  Assert(False, '不支持的动作');
end;

constructor TMailMulitAccountStorageCommand.Create(AMailAccountList: TStrings);
begin
  inherited Create;
  FMailAccountList := AMailAccountList;
end;

type
  TAccountWrapper = class(TComponent)
  private
    FAccount: TMailAccount;
  //published
    property Account: TMailAccount read FAccount write FAccount;
  end;

{ TMailAccount }

constructor TMailAccount.Create;
begin
  inherited;

  AccountGUID := CreateGUIDStr;
  AccountID := '';
  OwnerID := '';
  MailAddress := '';

  POPHost := '';
  FPOPPort  := 110;
  SMTPHost := '';
  FSMTPPort := 25;

  UserID := '';
  Password := '';
  DifferPOP := False;
  Verify := False;
  VerifyAccount := '';
  VerifyPassword := '';
  DeleteOnRetrieve := False;

  IsDefault := False;
end;

destructor TMailAccount.Destroy;
begin
  inherited;
end;

procedure TMailAccount.Assign(Source: TPersistent);
var
  lMailAccount: TMailAccount;
begin
  if Source is TMailAccount then
  begin
    lMailAccount := TMailAccount(Source);
    AccountGUID := lMailAccount.AccountGUID;
    AccountID := lMailAccount.AccountID;
    MailAddress := lMailAccount.MailAddress;
    OwnerID := lMailAccount.OwnerID;

    POPHost := lMailAccount.POPHost;
    POPPort := lMailAccount.POPPort;
    SMTPHost := lMailAccount.SMTPHost;
    SMTPPort := lMailAccount.SMTPPort;

    UserID := lMailAccount.UserID;
    Password := lMailAccount.Password;

    DifferPOP := lMailAccount.DifferPOP;
    Verify := lMailAccount.Verify;
    VerifyAccount := lMailAccount.VerifyAccount;
    VerifyPassword := lMailAccount.VerifyPassword;

    IsDefault := lMailAccount.IsDefault;
    DeleteOnRetrieve := lMailAccount.DeleteOnRetrieve;
  end
  else
    inherited Assign(Source);
end;

procedure TMailAccount.LoadPropertiesFromStream(AStream: TStream);
var
  lIniFile: TStreamXMLIniFile;
begin
  AStream.Position := 0;
  lIniFile := TStreamXMLIniFile.Create(AStream);
  try
    //将Ini中的值赋给对象的属性
    AccountGUID := lIniFile.ReadString(cAccountSection, 'AccountGUID', '');
    AccountID := lIniFile.ReadString(cAccountSection, 'AccountID', '');
    OwnerID := lIniFile.ReadString(cAccountSection, 'OwnerID', '');
    MailAddress := lIniFile.ReadString(cAccountSection, 'MailAddress', '');

    POPHost := lIniFile.ReadString(cAccountSection, 'POPHost', '');
    POPPort := lIniFile.ReadInteger(cAccountSection, 'POPPort', 110);
    SMTPHost := lIniFile.ReadString(cAccountSection, 'SMTPHost', '');
    SMTPPort := lIniFile.ReadInteger(cAccountSection, 'SMTPPort', 25);

    UserID := lIniFile.ReadString(cAccountSection, 'UserID', '');
    Password := lIniFile.ReadString(cAccountSection, 'Password', '');
    DifferPOP := lIniFile.ReadBool(cAccountSection, 'DifferPOP', False);
    Verify := lIniFile.ReadBool(cAccountSection, 'Verify', True);
    VerifyAccount := lIniFile.ReadString(cAccountSection, 'VerifyAccount', '');
    VerifyPassword := lIniFile.ReadString(cAccountSection, 'VerifyPassword', '');
    DeleteOnRetrieve := lIniFile.ReadBool(cAccountSection, 'DeleteOnRetrieve', True);

    IsDefault := lIniFile.ReadBool(cAccountSection, 'IsDefault', True);
  finally
    lIniFile.Free;
  end;
end;

procedure TMailAccount.SavePropertiesToStream(AStream: TStream);
var
  lIniFile: TStreamXMLIniFile;
begin
  lIniFile := TStreamXMLIniFile.Create(AStream);
  try
    //将对象属性的值赋给Ini
    lIniFile.WriteString(cAccountSection, 'AccountGUID', AccountGUID);
    lIniFile.WriteString(cAccountSection, 'AccountID', AccountID);
    lIniFile.WriteString(cAccountSection, 'OwnerID', OwnerID);
    lIniFile.WriteString(cAccountSection, 'MailAddress', MailAddress);

    lIniFile.WriteString(cAccountSection, 'POPHost', POPHost);
    lIniFile.WriteInteger(cAccountSection, 'POPPort', POPPort);
    lIniFile.WriteString(cAccountSection, 'SMTPHost', SMTPHost);
    lIniFile.WriteInteger(cAccountSection, 'SMTPPort', SMTPPort);

    lIniFile.WriteString(cAccountSection, 'UserID', UserID);
    lIniFile.WriteString(cAccountSection, 'Password', Password);
    lIniFile.WriteBool(cAccountSection, 'DifferPOP', DifferPOP);
    lIniFile.WriteBool(cAccountSection, 'Verify', Verify);
    lIniFile.WriteString(cAccountSection, 'VerifyAccount', VerifyAccount);
    lIniFile.WriteString(cAccountSection, 'VerifyPassword', VerifyPassword);
    lIniFile.WriteBool(cAccountSection, 'DeleteOnRetrieve', DeleteOnRetrieve);

    lIniFile.WriteBool(cAccountSection, 'IsDefault', IsDefault);
  finally
    lIniFile.Free;
  end;
end;

{ TMailAccounts }

function TMailAccounts.Add(AMailAccount: TMailAccount): Integer;
begin
  Result := inherited Add(AMailAccount);
end;

procedure TMailAccounts.Clear;
{var
  I: Integer; }
begin
{ for I := 0 to Count - 1 do
    Delete(I); }

  inherited Clear;
end;

function TMailAccounts.Extract(Item: TMailAccount): TMailAccount;
begin
  Result := TMailAccount(inherited Extract(Item));
end;

function TMailAccounts.First: TMailAccount;
begin
  Result := TMailAccount(inherited First);
end;

function TMailAccounts.GetItems(Index: Integer): TMailAccount;
begin
  Result := TMailAccount(inherited Items[Index]);
end;

function TMailAccounts.IndexOf(AMailAccount: TMailAccount): Integer;
begin
  Result := inherited Indexof(AMailAccount);
end;

procedure TMailAccounts.Insert(Index: Integer; AMailAccount: TMailAccount);
begin
inherited Insert(Index, AMailAccount);
end;

function TMailAccounts.Last: TMailAccount;
begin
  Result := TMailAccount(inherited Last);
end;

function TMailAccounts.Remove(AMailAccount: TMailAccount): Integer;
begin
  Result := inherited Remove(AMailAccount);
end;

procedure TMailAccounts.SetItems(Index: Integer;
  AMailAccount: TMailAccount);
begin
  inherited Items[Index] := AMailAccount;
end;

function TMailAccounts.FindAccountByAddress(
  const MailAddress: string): TMailAccount;
var
  I: Integer;
begin
  Result := nil;
  for I := 0 to Count - 1 do
    if Items[I].MailAddress = MailAddress then
    begin
      Result := Items[I];
      Exit;
    end;
end;

function TMailAccounts.FindAccountByGUID(
  const AccountGUID: string): TMailAccount;
var
  I: Integer;
begin
  Result := nil;
  for I := 0 to Count - 1 do
    if Items[I].AccountGUID = AccountGUID then
    begin
      Result := Items[I];
      Exit;
    end;
end;

function TMailAccounts.FindAccountByID(
  const AccountID: string): TMailAccount;
var
  I: Integer;
begin
  Result := nil;
  for I := 0 to Count - 1 do
    if Items[I].AccountID = AccountID then
    begin
      Result := Items[I];
      Exit;
    end;
end;

{ TMessage2Mail }

function ConvertPriority(const APriority: string): Integer;
begin
  Result := StrToIntDef(APriority, -1);
  if Result = -1 then
    Result := StrToIntDef(Copy(APriority, 1, Pos(' ', APriority) - 1), 3);
end;

procedure TMessage2Mail.MessageToMail(AMessage: TMessage; AMailInfo: TMailInfo;
  AMailContent: TStream; AMailAttachments: TList);
var
  lHasAttach: Boolean;
begin
  AMailInfo.From := AMessage.From;
  AMailInfo.Tos := AMessage.SendTo;
  AMailInfo.Cc := AMessage.CC;
  AMailInfo.Bcc := AMessage.BCC;
  AMailInfo.Subject := AMessage.Subject;
  AMailInfo.Date := TIBMailConst.MailDateToDelphiDate(AMessage.Date);
  AMailInfo.Priority := ConvertPriority(AMessage.Priority);

  ToMailAttachments(AMessage, lHasAttach, AMailAttachments, AMailInfo.AttachmentInfos);
  AMailInfo.HasAttach := lHasAttach;
  AMessage.SaveToStream(AMailContent);
end;

procedure TMessage2Mail.MailToMessage(AMailInfo: TMailInfo; AMailContent: TStream;
  AMailAttachments: TList; AMessage: TMessage);
var
  I: Integer;
  S: string;
  lStream: TStream;
begin
//  Assert(AMailAttachments.Count = AMailInfo.AttachmentInfos.Count, '');
  AMessage.LoadFromStream(AMailContent);
  for I := 0 to AMailAttachments.Count - 1 do
  begin
    lStream := TStream(AMailAttachments[I]);
    lStream.Position := 0;
    S := AMailInfo.AttachmentInfos[I];
    S := Copy(S, Pos('*', S) + 1, MaxInt);
    AMessage.AddAttachByIndex(S, I);
//    AMessage.AddAttachFromStream(S, lStream);
  end;
end;

procedure TMessage2Mail.ToMailAttachments(AMessage: TMessage;
  var HasAttach: Boolean; Attachments: TList; AttachmentInfos: TStrings);
var
  I: Integer;
  lStrings: TStrings;
  lStream: TStream;
begin
  HasAttach := False;
  lStrings := TStringList.Create;
  try
    AMessage.GetAttachFileNames(lStrings);

    Attachments.Clear;
    AttachmentInfos.Clear;

    for I := 0 to lStrings.Count - 1 do
    begin
      lStream := THugeMemoryStream.Create(0);
      TMessageItem(lStrings.Objects[I]).GetContent(lStream);
      Attachments.Add(lStream);
      AttachmentInfos.Add(Format('%d*%s', [lStream.Size, lStrings.Strings[I]]));
      HasAttach := True;

      AMessage.DeleteAttachment(lStrings.Strings[I]);
    end;
  finally
    lStrings.Free;
  end;
end;

{ TMailSetting }

procedure TMailSetting.LoadFromFile(const FileName: string);
var
  lIni: TIniFile;
  lStream: TStream;
begin
  lIni := TIniFile.Create(FileName);
  try
    lStream := THugeMemoryStream.Create(0);
    try
      lIni.ReadBinaryStream(TMailLibrary.GetOwnerID, ValueName, lStream);
      lStream.Position := 0;
      LoadFromStream(lStream);
    finally
      lStream.Free;
    end;
  finally
    lIni.Free;
  end;
end;

procedure TMailSetting.SaveToFile(const FileName: string);
var
  lIni: TIniFile;
  lStream: TStream;
begin
  try
    lIni := TIniFile.Create(FileName);
    try
      lStream := THugeMemoryStream.Create(0);
      try
        SaveToStream(lStream);
        lStream.Position := 0;
        lIni.WriteBinaryStream(TMailLibrary.GetOwnerID, ValueName, lStream);
      finally
        lStream.Free;
      end;
    finally
      lIni.Free;
    end;
  except
    //nothing todo
  end;
end;

constructor TSendedMailState.Create(const AMailGUID: String);
begin
  inherited Create;
  FMailGUID := AMailGUID;
end;

function TSendedMailState.getState: String;
var
  lReaded, lUnReaded: integer;

begin
  lReaded := 0;
  lUnReaded := 0;
  with TQuery.Create(nil) do
  try
    ConnectionString := cConnectionStr;
    if Connection.Meta.DataProvider = BizSchemaTypes.cDriver_ORACLE then
      CommandText := 'SELECT * FROM TMAILREFERENCE WHERE (BITAND(FSTATE,2) <> 2) AND FGUID = ' + SysUtils.QuotedStr(FMAILGUID)
    else
      CommandText := 'SELECT * FROM TMAILREFERENCE WHERE ((FSTATE&2) <> 2) AND FGUID = ' + SysUtils.QuotedStr(FMAILGUID);
    Open;
    while not EOF do
    begin
      if (FieldByName('FSTATE').AsInteger and msReaded) = msReaded then
        Inc(lReaded)
      else
        Inc(lUnReaded);
      if (lReaded > 0) and (lUnReaded > 0) then
      begin
        Result := cDepartReaded; //部分已读
        Exit;
      end;
      Next;
    end;
    if lReaded > 0 then
      Result := cAllReaded;//全部已读
    if lUnReaded > 0 then
      Result := cNoOneReaded; //全部未读
  finally
    Free;
  end;
end;

function TSendedMailState.getHints: String;
var
  lReaded, lUnReaded: TStrings;
  I: Integer;
  lReturn, lPersonName: String;
  lPerson: TPerson;
begin
  lReaded := TStringList.Create;
  lUnReaded := TStringList.Create;
  with TQuery.Create(nil) do
  try
    ConnectionString := cConnectionStr;
    if SameText(Connection.Meta.DataProvider, cDriver_ORACLE) then
      CommandText := 'SELECT * FROM TMAILREFERENCE WHERE (BITAND(FSTATE, 2) <> 2) AND FGUID = ' + SysUtils.QuotedStr(FMAILGUID)
    else
      CommandText := 'SELECT * FROM TMAILREFERENCE WHERE ((FSTATE & 2) <> 2) AND FGUID = ' + SysUtils.QuotedStr(FMAILGUID);
    Open;
    while not EOF do
    begin
      lPersonName := FieldByName('FOWNERGUID').AsString;
      lPerson := OrgSys.OrgSystem.FindPerson(FieldByName('FOWNERGUID').AsString);
      if Assigned(lPerson) then
        lPersonName := lPerson.DisplayName;
      if (FieldByName('FSTATE').AsInteger and msReaded) = msReaded then
        lReaded.Add(lPersonName)
      else
        lUnReaded.Add(lPersonName);
      Next;
    end;
    if lReaded.Count > 0 then
      Result := '已读人数:' + SysUtils.IntToStr(lReaded.Count) + #13 +#10;
    if lUnReaded.Count > 0 then
      Result := Result + '未读人数:' + SysUtils.IntToStr(lUnReaded.Count) + #13 + #10;

    Result := Result + #13 + #10;
    if lReaded.Count > 0 then
      Result := Result + '已读:';
    lReturn := '';  //计算换行
    for I := 0 to lReaded.Count - 1 do
    begin
      if I = lReaded.Count - 1 then
        Result := Result + lReaded[I]
      else
        Result := Result + lReaded[I] + ',' ;
      lReturn := lReturn + lReaded[I] + ',';
      if(I > 0) and (lReturn.Length >= 36) then
      begin
        lReturn := '';
        Result := Result + #13 + #10 +'      ';
      end;
    end;

    Result := Result + #13 + #10;
    if lUnReaded.Count > 0 then
      Result := Result + '未读:';
    lReturn := '';  //计算换行
    for I := 0 to lUnReaded.Count - 1 do
    begin
      if I = lUnReaded.Count - 1 then
        Result := Result + lUnReaded[I]
      else
        Result := Result + lUnReaded[I] + ',';
      lReturn := lReturn + lUnReaded[I] + ',';
      if (I>0) and (lReturn.Length >= 36) then
      begin
        Result := Result + #13 + #10 + '      ';
        lReturn := '';
      end;
    end;
  finally
    Free;
    lUnReaded.Free;
    lReaded.Free;
  end;
end;

end.
回复 支持 反对

使用道具 举报

发表于 2008-3-4 09:14:35 | 显示全部楼层
建议楼主升级到3002版本
回复 支持 反对

使用道具 举报

 楼主| 发表于 2008-3-5 10:14:57 | 显示全部楼层
啥意思啊,我这不已经是3002版本了么?
.                                                                                                                                                                                          .
.                                                                                                                                                                                          .
回复 支持 反对

使用道具 举报

发表于 2008-3-5 10:26:33 | 显示全部楼层
我是针对楼主23楼提的问题,做的回答,
回复 支持 反对

使用道具 举报

 楼主| 发表于 2008-4-16 14:27:11 | 显示全部楼层
呵呵,我23楼的意思是我们在2827版本时候发的邮件,后来升级到3002版本了,再转发这个邮件的时候附件空,这个可能你们不太好测试吧,呵呵。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Justep Inc.

GMT+8, 2025-7-11 02:22 , Processed in 0.057364 second(s), 14 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表