⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 unitmain.pas

📁 Delphi版飞信源代码 用Delphi实现飞信功能
💻 PAS
📖 第 1 页 / 共 3 页
字号:
unit UnitMain;

interface

uses
  UnitSHA1, UnitMd5, IniFiles, SHFolder, EncdDecd, IdZLibCompressorBase,
{$IFDEF UNICODE}IdCompressorZLib, {$ELSE}IdCompressorZLibEx, {$ENDIF}
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, IdTCPConnection, IdTCPClient, IdCookieManager,
  IdHTTP, StdCtrls, IEHTTP3, IdHashMessageDigest, IdGlobal, IdHash, xmldom,
  XMLIntf, msxmldom, XMLDoc, Buttons, ExtCtrls, ComCtrls, jpeg, IdBaseComponent,
  IdComponent;

{$IFNDEF UNICODE}
type
  RawByteString = AnsiString;
{$ENDIF}

const
  FORM_CAPTION = 'GreenFetion V0.22';

  LOGON_REQUEST_INVITE = 'fetion.com.cn SIP-C/2.0';
  LOGON_REQUEST_ARG = '<args><device type="PC" version="33" client-version="3.3.0370" />' +
    '<caps value="simple-im;im-session;temp-group;personal-group" /><events value="contact;permission;system-message;personal-group" /><user-info attributes="all" /><presence><basic value="400" desc="" /></presence></args>';
  LOGON_GETCONTACTLIST_ARG = '<args><contacts><buddy-lists /><buddies attributes="all" /><mobile-buddies attributes="all" /><chat-friends /><blacklist /></contacts></args>';
  LOGON_GETCONTACTINFO_ARG = '<args><contacts attributes="provisioning;impresa;mobile-no;nickname;name;gender;portrait-crc;ivr-enabled" extended-attributes="score-level">'; //<contact uri="sip:689685467@fetion.com.cn;p=9805" version="12" /></contacts></args>';

//  SALT = #$77+#$7A+#$6D+#$03;
var
//  SALT : array[1..4] of ansichar; // = #$FD + #$A5 + ansichar(#$E5) + #$01;
//  SALT : array[0..3] of byte = ($FD, $A5, $E5, $01);
  SALT: array[0..3] of byte = ($A8, $4C, $BF, $00);

const
  MSG_FONT_SIZE = 10;
  MSG_FONT_COLOR = clBlack;
  SYSMSG_FONT_SIZE = 8;
  SYSMSG_FONT_COLOR = clGreen;

type
  // 用户信息
  TUserInfo = record
    Sid: string; // 飞信标识
    IsFetionUser: Boolean;
    Name, NickName, LocalName, MobileNum: string;
    Nation, Province, City: string;
    Group: Integer;
  end;

  // 联系人列表管理
  TContactList = class
  private
    FUserList: array of TUserInfo;
    function GetCount: Integer;
    function GetItem(itemIndex: Integer): TUserInfo;
    function GetUserName(itemIndex: Integer): string;
  public
    procedure Clear;
    procedure AddUser(aSid, aLocalName: string; IsFetionUser: Boolean; aGroup: Integer; aMobileNum: string = ''; aNickName: string = ''; aName: string = '');
    procedure UpdateUserNickName(aSid, aNickName, aName, aMobileNum: string);
    function GetUserSidByName(aName: string): string;
  public
    property Count: Integer read GetCount;
    property Items[itemIndex: Integer]: TUserInfo read GetItem;
    property UserName[itemIndex: Integer]: string read GetUserName;
  end;

  TFormMain = class(TForm)
    IdTCPClient1: TIdTCPClient;
    reLogMsg: TRichEdit;
    Panel1: TPanel;
    Panel2: TPanel;
    mmMsg: TMemo;
    btSendMsg: TBitBtn;
    Label1: TLabel;
    cbContactList: TComboBox;
    tmRegister: TTimer;
    IdHTTP1: TIdHTTP;
    Panel3: TPanel;
    Image1: TImage;
    Label2: TLabel;
    btAddContact: TBitBtn;
    btRefreshContactList: TBitBtn;
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure btSendMsgClick(Sender: TObject);
    procedure tmRegisterTimer(Sender: TObject);
    procedure Label2Click(Sender: TObject);
    procedure btAddContactClick(Sender: TObject);
    procedure btRefreshContactListClick(Sender: TObject);
  private
    { Private declarations }
    XMLReader: TXMLDocument;

    FPhoneNum: string;
    FPassWord: string;
    FNickName: string;
    FSSIC: string;
    FUserURI: string; // 用户飞信标识
    FUserSid: string; // 飞信号
    FDomain: string; // 飞信标识中的域名
    FSalt: string;
    FNonce: string;
    FCNonce: string;

    FSIPCServer: string;
    FSIPCPort: Integer;
    FSSISignInURL: string;
    FGetUriURL: string;

    FContactList: TContactList;

    FCall: Integer;
    FRegisterCount: Integer;
    function GetNextCall: Integer;
    function GetNextRegisterCount: Integer;
    function BuildSIPRequest(Cmd: string; fields: array of string;
      arg: string; CmdTryCount: Integer = 1): string;
    function GetSIPResponse(Cmd: string; var ResponseMsg: string): Boolean;

    function hash_password(pwd: string): string;
    function calc_salt(pwd: string): string;
    function calc_cnonce: string;
    function calc_response(sid, domain, pwd, nonce, cnonce: string): string;
    function build_reponse_A: string;

    function SendMessage(uri, msg: string): Boolean;
    procedure RetrivePersonalInfo; // 获取自己的信息
    procedure RetriveContractInfo(aSubscribeResponse: string); // 从预定信息回复中获取某联系人信息
    procedure RetriveContractList;
    procedure RefreshContactList;
    function AddContact(aURI: string; aMsg: string): boolean; //  添加联系人,返回新添加的联系人的信息

    procedure AddLogMsg(msg: string; FontColor: TColor);
    procedure AddSysMsg(msg: string; FontColor: TColor);

    function SocketReadLn: string;
    function SocketReadString(aStrLen: integer): string;
    procedure SocketWrite(aStr: string);

    function ReadUserConfig(aUserPhoneNum: string): boolean;
    function WriteUserConfig(aUserPhoneNum: string; Update: Boolean = True): boolean;
    procedure InitConfigFromFetion; // 从飞信配置文件读用户登录服务器IP信息
  public
    { Public declarations }

    function Login(PhoneNum, Password: string): Boolean;
    function RetriveUserInfo(aURI: string): TUserInfo; // 获取指定用户的信息
    function RetriveURIFromUID(aUid: string): string; // 获取指定飞信号对应的URI
  end;

var
  FormMain: TFormMain;

implementation

{$R *.dfm}

uses UnitLogin, About, UnitAddContact;

function GetSubStringBetween(aString, beginstr, endstr: string): string;
var Len, index, beginPos: Integer;
begin
  Len := 0;
  if Pos(beginstr, aString) <= 0 then
    Exit;
  if Pos(endstr, aString) <= 0 then
    Exit;

  beginPos := Pos(beginstr, aString) + Length(beginstr);
  index := beginPos;
  while aString[index] <> endstr[1] do
  begin
    Inc(index);
    Inc(Len);
  end;
  Result := Copy(aString, beginPos, Len);
end;

function TFormMain.AddContact(aURI: string; aMsg: string): boolean;
var ResponseMsg: string;

  procedure ShowNodeInfo(aNode: IXMLNode; IsFetionUser: boolean);
  var uri, localName: string;
    group: Integer;
  begin
    if aNode = nil then
      Exit;
    uri := '';
    localName := '';
    group := 0;
    if aNode.HasAttribute('uri') then
      uri := aNode.Attributes['uri'];
    if aNode.HasAttribute('local-name') then
      localName := aNode.Attributes['local-name'];
    if aNode.HasAttribute('buddy-lists') then
      group := aNode.Attributes['buddy-lists'];
    if uri <> '' then
      FContactList.AddUser(uri, localName, IsFetionUser, group);
  end;
  procedure ExploreNode(aNode: IXMLNode);
  var I, J: Integer;
  begin
    if aNode.NodeName = 'buddy' then
      ShowNodeInfo(aNode, True);
    if aNode.NodeName = 'mobile-buddy' then
      ShowNodeInfo(aNode, False);

    for I := 0 to aNode.ChildNodes.Count - 1 do
    begin
      ExploreNode(aNode.ChildNodes[I]);
    end;
  end;
begin
  Result := False;
  if not IdTCPClient1.Connected then
    Exit;

  SocketWrite(BuildSIPRequest('S', ['N: AddBuddy'],
    '<args><contacts><buddies><buddy uri="' + aUri + '" buddy-lists="1" desc="' + aMsg + '" expose-mobile-no="1" expose-name="1" /></buddies></contacts></args>'));
  if GetSIPResponse('S', ResponseMsg) then
  begin
    Result := True;
    Exit;
  end;

  // 添加联系人不成功则尝试按手机号码添加联系人
  SocketWrite(BuildSIPRequest('S', ['N: AddMobileBuddy'],
    '<args><contacts><mobile-buddies><mobile-buddy uri="' + aUri + '" buddy-lists="1" desc="' + aMsg + '" invite="1" /></mobile-buddies></contacts></args>'));
  if GetSIPResponse('S', ResponseMsg) then
  begin
    Result := True;
    Exit;
  end;
end;

procedure TFormMain.AddLogMsg(msg: string; FontColor: TColor);
var
  P: Integer;
begin
  P := Length(reLogMsg.Text); //Keep Append Position
  with reLogMsg do
  begin
    Lines.Add(msg);
    Lines.Add('--------------------');
    SelStart := P;
    SelLength := Length(reLogMsg.Text) - P;
    SelAttributes.Color := FontColor;
    SelAttributes.Size := MSG_FONT_SIZE;
  end;
  Windows.SendMessage(reLogMsg.Handle, WM_VSCROLL, SB_BOTTOM, 0);
end;

procedure TFormMain.AddSysMsg(msg: string; FontColor: TColor);
var
  P: Integer;
begin
  P := Length(reLogMsg.Text); //Keep Append Position
  with reLogMsg do
  begin
    Lines.Add('【系统提示】:' + msg);
    SelStart := P;
    SelLength := Length(reLogMsg.Text) - P;
    SelAttributes.Color := FontColor;
    SelAttributes.Size := SYSMSG_FONT_SIZE;
  end;
  Windows.SendMessage(reLogMsg.Handle, WM_VSCROLL, SB_BOTTOM, 0);
end;

procedure TFormMain.btAddContactClick(Sender: TObject);
begin
  FormAddContact.edPSMsg.Text := FNickName;
  if FormAddContact.ShowModal = mrOK then
    if AddContact(FormAddContact.AddUri, FormAddContact.PSMsg) then
      AddSysMsg('添加联系人成功。' + #$0D + #$0A, clGreen)
    else
      AddSysMsg('添加联系人失败。' + #$0D + #$0A, clRed);
end;

procedure TFormMain.btRefreshContactListClick(Sender: TObject);
begin
  RefreshContactList;
end;

procedure TFormMain.btSendMsgClick(Sender: TObject);
begin
  if mmMsg.Text = '' then
    Exit;
  AddLogMsg(mmMsg.Text, clBlack);
  if SendMessage(FContactList.Items[cbContactList.ItemIndex].Sid, mmMsg.Text) then
    AddSysMsg('短信发送完毕!' + #$0D + #$0A, clGreen)
  else
    AddSysMsg('短信发送失败!' + #$0D + #$0A, clRed);
  mmMsg.Clear;

end;

function TFormMain.BuildSIPRequest(Cmd: string; fields: array of string;
  arg: string; CmdTryCount: Integer = 1): string;
var I: Integer;
  UTF8arg: RawByteString;
begin
  Result := Cmd + ' ' + LOGON_REQUEST_INVITE;
  Result := Result + #$D + #$A + 'F: ' + FUserSid;
  if Cmd = 'R' then
  begin
    Result := Result + #$D + #$A + 'I: 1';
  end else
  begin
    if CmdTryCount = 1 then
      Result := Result + #$D + #$A + 'I: ' + IntToStr(GetNextCall())
    else
      Result := Result + #$D + #$A + 'I: ' + IntToStr(FCall);
  end;
  Result := Result + #$D + #$A + 'Q: ' + IntToStr(CmdTryCount) + ' ' + Cmd;
  for I := 0 to Length(fields) - 1 do
    Result := Result + #$D + #$A + fields[I];
  UTF8arg := UTF8Encode(arg);
  if Length(UTF8arg) > 0 then
  begin
    Result := Result + #$D + #$A + 'L: ' + IntToStr(Length(UTF8arg))
      + #$D + #$A + #$D + #$A
      + arg;
  end else
  begin
    Result := Result + #$D + #$A + #$D + #$A;
  end;
end;


function TFormMain.GetSIPResponse(Cmd: string; var ResponseMsg: string): Boolean;
var CmdLine: string;
  // 读取一个完整的SIP响应,并返回SIP响应对应的命令
  function ReadASIPResponse(var SIPMsg: string): string;
  var NewLine, SIPCmd, SubCmd: string;
    tmpIndex: Integer;
    MsgLength: Integer;
  begin
    Result := '';
    MsgLength := 0;
    CmdLine := SocketReadLn(); // SIP 响应头
    if Length(CmdLine) <= 0 then
      Exit;
    repeat
      NewLine := SocketReadLn();
      if Length(NewLine) <= 0 then
        Break;
      if UpperCase(NewLine[1]) = 'Q' then
      begin
          // 提取SIP命令
        tmpIndex := Pos(' ', NewLine);
        tmpIndex := PosIdx(' ', NewLine, tmpIndex + 1);
        Result := Copy(NewLine, tmpIndex + 1, MaxInt);
      end;
      if UpperCase(NewLine[1]) = 'N' then
      begin
          // 提取SIP子命令
        tmpIndex := Pos(' ', NewLine);
        SubCmd := Copy(NewLine, tmpIndex + 1, MaxInt);
      end;
      if UpperCase(NewLine[1]) = 'L' then
      begin
          // 获取SIP消息长度
        tmpIndex := Pos(' ', NewLine);
        MsgLength := StrToIntDef(Copy(NewLine, tmpIndex + 1, MaxInt), 0);
      end;
      if UpperCase(NewLine[1]) = 'W' then
      begin
          // 获取登录认证时服务器提供的Nonce
        FNonce := GetSubStringBetween(NewLine, 'nonce="', '"');
      end;
    until False;

    if MsgLength <= 0 then
      Exit;

    // 读取SIP消息
    SIPMsg := SocketReadString(MsgLength);

    // 获取联系人信息
    if Result = 'BN' then
      if SubCmd = 'contact' then
        RetriveContractInfo(SIPMsg);
  end;
begin
  Result := False;
  ResponseMsg := '';
  try
    // 读取与命令无关的响应
    while ReadASIPResponse(ResponseMsg) <> Cmd do ;

    if Pos('200 OK', CmdLine) > 0 then
      Result := True
    else
      if Pos('280 Send SMS OK', CmdLine) > 0 then
        Result := True;
  except
  end;
end;

function TFormMain.build_reponse_A: string;
begin
  FSalt := calc_salt(FPassWord);
  FCNonce := calc_cnonce;
//  Result := 'Digest algorithm="SHA1-sess",response="'
  Result := 'Digest '
//    + 'algorithm="MD5-sess",'
  + 'algorithm="SHA1-sess",'
    + 'response="' + calc_response(FUserSid, FDomain, FPassWord, FNonce, FCNonce)
    + '",cnonce="' + FCNonce
    + '",salt="' + FSalt
    + '",ssic="' + FSSIC
    + '"';
end;

function TFormMain.hash_password(pwd: string): string;
var
  SHA1Context: TSHA1Context;
  SHA1Digest: TSHA1Digest;
  byteBuf: TBytes;
  r: array[0..256] of byte;
begin
  FillMemory(@r[0], 257, 0);
{$IFDEF UNICODE}
  byteBuf := ToBytes(pwd, Length(pwd));
{$ELSE}
  byteBuf := ToBytes(pwd);
{$ENDIF}

  SHA1Init(SHA1Context);
  SHA1Update(SHA1Context, PAnsiChar(@byteBuf[0]), Length(byteBuf));
  SHA1Final(SHA1Context, SHA1Digest);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -