unit COMController;
{
    Copyright 2003, Peter Millard

    This file is part of Exodus.

    Exodus is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    Exodus is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Exodus; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
}

{$WARN SYMBOL_PLATFORM OFF}

interface

uses
  XMLTag, Unicode, Menus,
  Windows, Classes, ComObj, ActiveX, AnyQCOM_TLB, StdVcl;

type
  TAnyQController = class(TAutoObject, IAnyQController)
  protected
    function Get_Connected: WordBool; safecall;
    function Get_Server: WideString; safecall;
    function Get_Username: WideString; safecall;
    procedure AddRosterItem(const jid, nickname, group: WideString); safecall;
    procedure ChangePresence(const Show, Status: WideString;
      Priority: Integer); safecall;
    function isRosterJID(const jid: WideString): WordBool; safecall;
    function isSubscribed(const jid: WideString): WordBool; safecall;
    function RegisterCallback(const xpath: WideString;
      const callback: IAnyQPlugin): Integer; safecall;
    procedure RemoveRosterItem(const jid: WideString); safecall;
    procedure Send(const xml: WideString); safecall;
    procedure UnRegisterCallback(callback_id: Integer); safecall;
    procedure GetProfile(const jid: WideString); safecall;
    procedure StartChat(const jid, resource, nickname: WideString); safecall;
    procedure CreateDockableWindow(HWND: Integer; const Caption: WideString);
      safecall;
    function addPluginMenu(const Caption: WideString): WideString; safecall;
    procedure removePluginMenu(const ID: WideString); safecall;
    procedure monitorImplicitRegJID(const JabberID: WideString;
      FullJID: WordBool); safecall;
    function getAgentService(const Server, Service: WideString): WideString;
      safecall;
    procedure getAgentList(const Server: WideString); safecall;
    function generateID: WideString; safecall;
    function Get_IsInvisible: WordBool; safecall;
    function Get_IsPaused: WordBool; safecall;
    function Get_Port: Integer; safecall;
    function Get_PresenceShow: WideString; safecall;
    function Get_PresenceStatus: WideString; safecall;
    function Get_Priority: Integer; safecall;
    function Get_Resource: WideString; safecall;
    function isBlocked(const JabberID: WideString): WordBool; safecall;
    procedure Block(const JabberID: WideString); safecall;
    procedure Connect; safecall;
    procedure Disconnect; safecall;
    procedure UnBlock(const JabberID: WideString); safecall;
    function getPrefAsBool(const Key: WideString): WordBool; safecall;
    function getPrefAsInt(const Key: WideString): Integer; safecall;
    function getPrefAsString(const Key: WideString): WideString; safecall;
    procedure setPrefAsBool(const Key: WideString; Value: WordBool); safecall;
    procedure setPrefAsInt(const Key: WideString; Value: Integer); safecall;
    procedure setPrefAsString(const Key_, Value: WideString); safecall;
    function findChat(const JabberID, Resource: WideString): Integer; safecall;
    procedure startInstantMsg(const JabberID: WideString); safecall;
    procedure startRoom(const RoomJID, Nickname, Password: WideString);
      safecall;
    procedure startSearch(const SearchJID: WideString); safecall;
    procedure showJoinRoom(const RoomJID, Nickname, Password: WideString);
      safecall;
    procedure startBrowser(const BrowseJID: WideString); safecall;
    procedure showCustomPresDialog; safecall;
    procedure showDebug; safecall;
    procedure showLogin; safecall;
    procedure showPrefs; safecall;
    procedure showToast(const Message: WideString; wndHandle,
      imageIndex: Integer); safecall;
    procedure setPresence(const Show, Status: WideString; Priority: Integer);
      safecall;
    function Get_Roster: IAnyQRoster; safecall;
    function Get_PPDB: IAnyQPPDB; safecall;
    function registerBrowseNS(const Namespace: WideString): WideString;
      safecall;
    function registerDiscoFeature(const Feature: WideString): WideString;
      safecall;
    function registerDiscoItem(const JabberID, Name: WideString): WideString;
      safecall;
    procedure removeBrowseNS(const ID: WideString); safecall;
    procedure removeDiscoFeature(const ID: WideString); safecall;
    procedure removeDiscoItem(const ID: WideString); safecall;
    function registerPresenceXML(const XML: WideString): WideString; safecall;
    procedure removePresenceXML(const ID: WideString); safecall;
    procedure trackWindowsMsg(Message: Integer); safecall;
    function addContactMenu(const Caption: WideString): WideString; safecall;
    procedure removeContactMenu(const ID: WideString); safecall;
    function getActiveContact: WideString; safecall;
    function getActiveGroup: WideString; safecall;
    function getActiveContacts(Online: WordBool): WideString; safecall;
    function Get_LocalIP: WideString; safecall;
    procedure setPluginAuth(const AuthAgent: IAnyQAuth); safecall;
    procedure setAuthenticated(Authed: WordBool; const XML: WideString);
      safecall;
    procedure setAuthJID(const Username, Host, Resource: WideString); safecall;
    function addMessageMenu(const Caption: WideString): WideString; safecall;
    procedure SkinForm(aHandle: Integer); safecall;
    { Protected declarations }
  private
    _menu_items: TWideStringList;
    _roster_menus: TWidestringlist;
    _msg_menus: TWidestringList;

  public
    constructor Create();
    destructor Destroy(); override;

    procedure fireNewChat(jid: WideString; AnyQChat: IAnyQChat);
    procedure fireNewRoom(jid: Widestring; AnyQChat: IAnyQChat);
    procedure fireNewOutgoingIM(jid: Widestring; AnyQChat: IAnyQChat);
    procedure fireMenuClick(Sender: TObject);
    procedure fireRosterMenuClick(Sender: TObject);
    function fireIM(Jid: Widestring; var Body: Widestring;
      var Subject: Widestring; xtags: Widestring): Widestring;
    procedure fireMsgMenuClick(idx: integer; jid: Widestring;
      var Body: Widestring; var Subject: Widestring);

    procedure populateMsgMenus(parent: TPopupMenu; event: TNotifyEvent);

  end;

  TPlugin = class
    com: IAnyQPlugin;
  end;

  TPluginProxy = class
  private
    _xpath: Widestring;
  public
    id: integer;
    com: OleVariant;
    constructor Create(xpath: Widestring; obj: OleVariant);
    destructor Destroy; override;
    procedure Callback(event: string; tag: TXMLTag);
  end;

  TMenuContainer = class
  public
    idx: integer;
    id: Widestring;
    caption: Widestring;
  end;


// Forward declares for plugin utils
procedure InitPlugins();
function LoadPlugin(com_name: string): boolean;
procedure UnloadPlugins();
procedure ConfigurePlugin(com_name: string);
procedure ReloadPlugins(sl: TWidestringlist);


resourcestring
  sPluginErrCreate = '插件不能创建. (%s)';
  sPluginErrNoIntf = '不支持的插件类型. (%s)';
  sPluginErrInit = '插件无法初始化. (%s)';
  sPluginRemove = '下次启动时不再启动该插件?';

implementation

uses
  ExResponders, ExSession,
  Chat, ChatController, JabberID, MsgRecv, Room, Browser, Jud,
  ChatWin, JoinRoom, CustomPres, RiserWindow, Debug,
  COMChatController, Dockable, Agents,
  Jabber1, Session, Roster, RosterWindow, PluginAuth, PrefController,
  Controls, Dialogs, Variants, Forms, SysUtils, ComServ, Options, Math;

var
  plugs: TStringList;
  proxies: TStringList;

{---------------------------------------}
{---------------------------------------}
{---------------------------------------}

procedure InitPlugins();
var
  s, ok: TWideStringlist;
  i: integer;

  procedure CheckPluginDll(dll: WideString;
    selected: TWidestringlist);
  var
    lib: ITypeLib;
    idx, i, j: integer;
    tinfo, iface: ITypeInfo;
    tattr, iattr: PTypeAttr;
    r: cardinal;
    libname, obname, doc: WideString;
    LibHandle: THandle;
    RegProc: TRegProc;
  begin
    LibHandle := LoadLibrary(PChar(string(dll)));
    if LibHandle = 0 then
      exit;
    try
      @RegProc := GetProcAddress(LibHandle, 'DllRegisterServer');
      if @RegProc = nil then
        exit;

      if RegProc <> 0 then
        exit;
    finally
      FreeLibrary(LibHandle);
    end;

    // load the .dll.  This SHOULD register the bloody thing if it's not, but that
    // doesn't seem to work for me.
    OleCheck(LoadTypeLibEx(PWideChar(dll), REGKIND_REGISTER, lib));

    // get the project name
    OleCheck(lib.GetDocumentation(-1, @libname, nil, nil, nil));

    // for each type in the project
    for i := 0 to lib.GetTypeInfoCount() - 1 do
    begin
      // get the info about the type
      OleCheck(lib.GetTypeInfo(i, tinfo));

      // get attributes of the type
      OleCheck(tinfo.GetTypeAttr(tattr));
      // is this a coclass?
      if (tattr.typekind <> TKIND_COCLASS) then continue;

      // for each interface that the coclass implements
      for j := 0 to tattr.cImplTypes - 1 do
      begin
        // get the type info for the interface
        OleCheck(tinfo.GetRefTypeOfImplType(j, r));
        OleCheck(tinfo.GetRefTypeInfo(r, iface));

        // get the attributes of the interface
        OleCheck(iface.GetTypeAttr(iattr));

        // is this the IAnyQPlugin interface?
        if (IsEqualGUID(iattr.guid, AnyQCOM_TLB.IID_IAnyQPlugin)) then
        begin
          // oho!  it IS.  Get the name of this coclass, so we can show
          // what we did.  Get the doc string, just to show off.
          OleCheck(tinfo.GetDocumentation(-1, @obname, @doc, nil, nil));
          // SysFreeString of obname and doc needed?  In C, yes, but here?
          selected.Add(libname + '.' + obname);
        end;
        iface.ReleaseTypeAttr(iattr);
      end;
      tinfo.ReleaseTypeAttr(tattr);
    end;
  end;

  procedure scanPluginDir(selected: TWidestringList);
  var
    dir: Widestring;
    sr: TSearchRec;
  begin
    dir := ExtractFilePath(Application.ExeName);
    if (not DirectoryExists(dir)) then exit;
    if (FindFirst(dir + '\\*.dll', faAnyFile, sr) = 0) then begin
      repeat
        try
          CheckPluginDll(dir + sr.Name, selected);
        except
        end;
      until FindNext(sr) <> 0;
      FindClose(sr);
    end;
  end;
begin
    // load all of the plugins listed in the prefs
  s := TWideStringlist.Create();
  ok := TWidestringList.Create();
  MainSession.Prefs.fillStringList('plugin_selected', s);

  for i := 0 to s.count - 1 do begin
    if (LoadPlugin(s[i]) = false) then begin
            // remove from list?
      if (MessageBox(Application.Handle, Pchar(sPluginRemove), '确认', MB_YESNO or MB_ICONQUESTION) = mrNo) then
        ok.Add(s[i]);
    end
    else
      ok.Add(s[i]);
  end;

    // re-save the "ok" ones.
  if (ok.Count <> s.Count) then
    MainSession.Prefs.setStringlist('plugin_selected', ok);

  s.Clear; {}
  scanPluginDir(s);
  for i := 0 to s.count - 1 do begin
    if (LoadPlugin(s[i]) = false) then
    begin
      ShowRiserWindow(nil, '无法初始化' + s[i], ico_info);
    end
  end;

  s.Free();
  ok.Free();
end;

{---------------------------------------}

function LoadPlugin(com_name: string): boolean;
var
  idisp: IDispatch;
  plugin: IAnyQPlugin;
  p: TPlugin;
  msg: Widestring;
begin
    // Fire up an instance of the specified COM object
  Result := false;
  if (plugs.indexof(com_name) > -1) then exit;

  try
    idisp := CreateOleObject(com_name);
  except
    on EOleSysError do begin
      msg := Format(sPluginErrCreate, [com_name]);
      ShowRiserWindow(nil, msg, ico_info);
            //MessageBox(Application.Handle,Pchar(msg), '错误', MB_OK OR MB_ICONERROR);
      exit;
    end;
  end;

  try
    plugin := IUnknown(idisp) as IAnyQPlugin;
  except
    on EIntfCastError do begin
      msg := Format(sPluginErrNoIntf, [com_name]);
      ShowRiserWindow(nil, msg, ico_info);
            //MessageBox(Application.Handle,Pchar(msg), '错误', MB_OK  OR MB_ICONERROR);
      exit;
    end;
  end;

  p := TPlugin.Create();
  p.com := plugin;
  plugs.AddObject(com_name, p);
  try
    p.com.Startup(ExComController);
  except
    msg := Format(sPluginErrInit, [com_name]);
    ShowRiserWindow(nil, msg, ico_info);
        //MessageBox(Application.Handle,Pchar(msg), '错误', MB_OK OR MB_ICONERROR);
    exit;
  end;

  Result := true;
end;

{---------------------------------------}

procedure ConfigurePlugin(com_name: string);
var
  idx: integer;
  p: TPlugin;
begin
    //
  idx := plugs.IndexOf(com_name);
  if (idx < 0) then begin
    LoadPlugin(com_name);
    idx := plugs.IndexOf(com_name);
  end;

  if (idx < 0) then begin
    ShowRiserWindow(nil, '插件无法初始化或配置。', ico_info);
        //MessageBox(application.Handle,'插件无法初始化或配置。',
        //    '错误', MB_OK  OR MB_ICONERROR);
    exit;
  end;

  p := TPlugin(plugs.Objects[idx]);
  p.com.Configure();
end;

{---------------------------------------}

procedure ReloadPlugins(sl: TWidestringlist);
var
  i, idx: integer;
  loaded: TStringlist;
  p: TPlugin;
begin
    // load all plugins listed, if they are not already loaded.
  loaded := TStringlist.Create();
  for i := 0 to sl.Count - 1 do begin
    idx := plugs.IndexOf(sl[i]);
    if (idx < 0) then
      LoadPlugin(sl[i]);
    loaded.Add(sl[i]);
  end;

    // unload any plugins not in the loaded list
  for i := plugs.Count - 1 downto 0 do begin
    idx := loaded.IndexOf(plugs[i]);
    if (idx < 0) then begin
            // unload the plugin
      p := TPlugin(plugs.Objects[i]);
      p.com.Shutdown();
      plugs.Delete(i);
    end;
  end;
  loaded.Free();
end;

{---------------------------------------}

procedure UnloadPlugins();
var
  pp: TPlugin;
  i: integer;
begin
    // kill all of the various plugins which are loaded.
  for i := proxies.Count - 1 downto 0 do
    TPluginProxy(proxies.Objects[i]).Free();

    // pgm Dec 12, 2002 - Don't free pp, or call pp.com._Release,
    // or else bad things can happen here... assume that mem is getting
    // cleared.
  for i := plugs.Count - 1 downto 0 do begin
    pp := TPlugin(plugs.Objects[i]);
    plugs.Delete(i);
    pp.com.Shutdown();
  end;

  plugs.Clear();
end;

{---------------------------------------}
{---------------------------------------}
{---------------------------------------}

constructor TPluginProxy.Create(xpath: Widestring; obj: OleVariant);
begin
  inherited Create;

  _xpath := xpath;

  id := MainSession.RegisterCallback(Self.Callback, xpath);
  com := obj;

  proxies.AddObject(IntToStr(id), Self)
end;

{---------------------------------------}

destructor TPluginProxy.Destroy;
var
  idx: integer;
begin
  if (MainSession <> nil) and (id <> -1) then
    MainSession.UnRegisterCallback(id);

  idx := proxies.IndexOfObject(Self);
  if (idx <> -1) then
    proxies.Delete(idx);

  inherited Destroy;
end;

{---------------------------------------}

procedure TPluginProxy.Callback(event: string; tag: TXMLTag);
var
  plugin: IAnyQPlugin;
begin
    // call the plugin back
  plugin := IUnknown(com) as IAnyQPlugin;
  plugin.Process(_xpath, event, tag.xml);
end;


{---------------------------------------}
{---------------------------------------}
{---------------------------------------}

constructor TAnyQController.Create();
begin
  inherited Create();
  _menu_items := TWidestringList.Create();
  _roster_menus := Twidestringlist.Create();
  _msg_menus := TWidestringlist.Create();
end;

{---------------------------------------}

destructor TAnyQController.Destroy();
begin
    // xxx: cleanup these menu items???
  _menu_items.Free();
  _roster_menus.Free();
  _msg_menus.Free();

  OutputDebugString('Destroying TAnyQController');

  inherited;
end;

{---------------------------------------}

procedure TAnyQController.fireNewChat(jid: WideString; AnyQChat: IAnyQChat);
var
  i: integer;
begin
  for i := 0 to plugs.count - 1 do
    TPlugin(plugs.Objects[i]).com.NewChat(jid, AnyQChat);
end;

{---------------------------------------}

procedure TAnyQController.fireNewOutgoingIM(jid: Widestring; AnyQChat: IAnyQChat);
var
  i: integer;
begin
  for i := 0 to plugs.Count - 1 do
    TPlugin(plugs.Objects[i]).com.NewOutgoingIM(jid, AnyQChat);
end;

{---------------------------------------}

procedure TAnyQController.fireNewRoom(jid: Widestring; AnyQChat: IAnyQChat);
var
  i: integer;
begin
  for i := 0 to plugs.Count - 1 do
    TPlugin(plugs.Objects[i]).com.NewRoom(jid, AnyQChat);
end;

{---------------------------------------}

function TAnyQController.fireIM(Jid: Widestring; var Body: Widestring;
  var Subject: Widestring; xtags: Widestring): Widestring;
var
  i: integer;
  xml: Widestring;
begin
    // xxx: pass along xtags to the plugins
  xml := '';
  for i := 0 to plugs.Count - 1 do
    xml := xml + TPlugin(plugs.Objects[i]).com.NewIM(jid, body, subject, xtags);
  Result := xml;
end;

{---------------------------------------}

procedure TAnyQController.fireMsgMenuClick(idx: integer; jid: Widestring;
  var Body: Widestring; var Subject: Widestring);
var
  i: integer;
begin
  if (idx >= _msg_menus.Count) then exit;
  for i := 0 to plugs.Count - 1 do
    TPlugin(plugs.Objects[i]).com.MsgMenuClick(_msg_menus[idx], jid,
      Body, Subject);
end;

{---------------------------------------}

function TAnyQController.Get_Connected: WordBool;
begin
  Result := MainSession.Active;
end;

{---------------------------------------}

function TAnyQController.Get_Server: WideString;
begin
  Result := MainSession.Profile.Server;
end;

{---------------------------------------}

function TAnyQController.Get_Username: WideString;
begin
  Result := MainSession.Profile.Username;
end;

{---------------------------------------}

procedure TAnyQController.AddRosterItem(const jid, nickname,
  group: WideString);
begin
  MainSession.roster.AddItem(jid, nickname, group, true);
end;

{---------------------------------------}

procedure TAnyQController.ChangePresence(const Show, Status: WideString;
  Priority: Integer);
begin
    // Change our presence
  MainSession.setPresence(Show, Status, Priority);
end;

{---------------------------------------}

function TAnyQController.isRosterJID(const jid: WideString): WordBool;
begin
  Result := (MainSession.Roster.Find(jid) <> nil);
end;

{---------------------------------------}

function TAnyQController.isSubscribed(const jid: WideString): WordBool;
var
  ritem: TJabberRosterItem;
begin
  Result := false;
  ritem := MainSession.Roster.Find(jid);
  if (ritem <> nil) then begin
    if (ritem.subscription = 'to') or
      (ritem.subscription = 'both') then
      Result := true;
  end;
end;

{---------------------------------------}

function TAnyQController.RegisterCallback(const xpath: WideString;
  const callback: IAnyQPlugin): Integer;
begin
  TPluginProxy.Create(xpath, callback);
end;

{---------------------------------------}

procedure TAnyQController.RemoveRosterItem(const jid: WideString);
begin
    // todo: plugin remove roster item
end;

{---------------------------------------}

procedure TAnyQController.Send(const xml: WideString);
begin
  MainSession.Stream.Send(xml);
end;

{---------------------------------------}

procedure TAnyQController.UnRegisterCallback(callback_id: Integer);
var
  idx: integer;
begin
  idx := proxies.indexOf(IntToStr(callback_id));
  if (idx <> -1) then begin
    TPluginProxy(proxies.Objects[idx]).Free;
    proxies.Delete(idx);
  end;
end;

{---------------------------------------}

procedure TAnyQController.GetProfile(const jid: WideString);
begin
    // todo: fetch profile for COM
end;

{---------------------------------------}

procedure TAnyQController.StartChat(const jid, resource,
  nickname: WideString);
begin
    // start chat
  StartChat(jid, resource, nickname);
end;

{---------------------------------------}

procedure TAnyQController.CreateDockableWindow(HWND: Integer;
  const Caption: WideString);
var
  f: TfrmDockable;
begin
    // subclass frmDockable, and re-parent
    // this HWND to the new form
  f := TfrmDockable.Create(Application);
  SetParent(HWND, f.Handle);
  f.ShowDefault();
  ShowWindow(HWND, SW_SHOW);
end;

{---------------------------------------}

function TAnyQController.addPluginMenu(
  const Caption: WideString): WideString;
var
  id: Widestring;
  mi: TMenuItem;
begin
    // add a new TMenuItem to the Plugins menu
  mi := TMenuItem.Create(frmAnyQ);
  frmAnyQ.Tools1.Add(mi);
//    frmAnyQ.mnuPlugins.Add(mi);
  mi.Caption := caption;
  mi.OnClick := frmAnyQ.mnuPluginDummyClick;
  id := 'plugin_' + IntToStr(_menu_items.Count);
  mi.Name := id;
  _menu_items.AddObject(id, mi);
  Result := id;
end;

{---------------------------------------}

procedure TAnyQController.removePluginMenu(const ID: WideString);
var
  idx: integer;
begin
  idx := _menu_items.IndexOf(id);
  if (idx >= 0) then begin
    TMenuItem(_menu_items.Objects[idx]).Free();
    _menu_items.Delete(idx);
  end;
end;

{---------------------------------------}

procedure TAnyQController.fireMenuClick(Sender: TObject);
var
  i, idx: integer;
begin
  idx := _menu_items.IndexOfObject(Sender);
  if (idx >= 0) then begin
    for i := 0 to plugs.count - 1 do
      TPlugin(plugs.Objects[i]).com.menuClick(_menu_items[idx]);
  end;
end;

{---------------------------------------}

procedure TAnyQController.fireRosterMenuClick(Sender: TObject);
var
  i, idx: integer;
begin
  idx := _roster_menus.indexOfObject(Sender);
  if (idx >= 0) then begin
    for i := 0 to plugs.count - 1 do
      TPlugin(plugs.Objects[i]).com.menuClick(_roster_menus[idx]);
  end;
end;

{---------------------------------------}

procedure TAnyQController.monitorImplicitRegJID(
  const JabberID: WideString; FullJID: WordBool);
begin
  ExRegController.MonitorJid(JabberID, FullJID);
end;

{---------------------------------------}

function TAnyQController.getAgentService(const Server,
  Service: WideString): WideString;
var
  al: TAgents;
  a: TAgentItem;
begin
  Result := '';
  al := MainSession.GetAgentsList(Server);
  if (al <> nil) then begin
    a := al.findService(Lowercase(Service));
    if (a <> nil) then
      Result := a.jid;
  end;
end;

{---------------------------------------}

procedure TAnyQController.getAgentList(const Server: WideString);
var
  atag: TXMLTag;
  al: TAgents;
begin
  al := MainSession.GetAgentsList(Server);
  if (al = nil) then begin
    al := MainSession.NewAgentsList(Server);
    al.Fetch(Server);
  end
  else begin
        // we already have it, fire now.
    atag := TXMLTag.Create('iq');
    atag.setAttribute('from', Server);
    MainSession.FireEvent('/session/agents', atag);
    atag.Free();
  end;
end;

{---------------------------------------}

function TAnyQController.generateID: WideString;
begin
  Result := MainSession.generateID();
end;

function TAnyQController.Get_IsInvisible: WordBool;
begin
  Result := MainSession.Invisible;
end;

function TAnyQController.Get_IsPaused: WordBool;
begin
  Result := MainSession.IsPaused;
end;

function TAnyQController.Get_Port: Integer;
begin
  Result := MainSession.Port;
end;

function TAnyQController.Get_PresenceShow: WideString;
begin
  Result := MainSession.Show;
end;

function TAnyQController.Get_PresenceStatus: WideString;
begin
  Result := MainSession.Status;
end;

function TAnyQController.Get_Priority: Integer;
begin
  Result := MainSession.Priority;
end;

function TAnyQController.Get_Resource: WideString;
begin
  Result := MainSession.Resource;
end;

function TAnyQController.isBlocked(const JabberID: WideString): WordBool;
begin
  Result := MainSession.IsBlocked(JabberID);
end;

{---------------------------------------}

procedure TAnyQController.Block(const JabberID: WideString);
var
  tmpjid: TJabberID;
begin
  tmpjid := TJabberID.Create(jabberID);
  MainSession.Block(tmpjid);
  tmpjid.Free();
end;

{---------------------------------------}

procedure TAnyQController.Connect;
begin
  if not MainSession.Active then
    MainSession.Connect();
end;

{---------------------------------------}

procedure TAnyQController.Disconnect;
begin
  if MainSession.Active then
    MainSession.Disconnect();
end;

{---------------------------------------}

procedure TAnyQController.UnBlock(const JabberID: WideString);
var
  tmpjid: TJabberID;
begin
  tmpjid := TJabberID.Create(JabberID);
  MainSession.UnBlock(tmpjid);
  tmpjid.Free();
end;

{---------------------------------------}

function TAnyQController.getPrefAsBool(const Key: WideString): WordBool;
begin
  Result := MainSession.Prefs.getBool(key);
end;

{---------------------------------------}

function TAnyQController.getPrefAsInt(const Key: WideString): Integer;
begin
  Result := MainSession.Prefs.getInt(key);
end;

{---------------------------------------}

function TAnyQController.getPrefAsString(
  const Key: WideString): WideString;
begin
  Result := MainSession.Prefs.getString(key);
end;

{---------------------------------------}

procedure TAnyQController.setPrefAsBool(const Key: WideString;
  Value: WordBool);
begin
  MainSession.Prefs.setBool(key, value);
end;

{---------------------------------------}

procedure TAnyQController.setPrefAsInt(const Key: WideString;
  Value: Integer);
begin
  MainSession.Prefs.setInt(key, value);
end;

{---------------------------------------}

procedure TAnyQController.setPrefAsString(const Key_, Value: WideString);
begin
  MainSession.Prefs.setString(Key_, value);
end;

{---------------------------------------}

function TAnyQController.findChat(const JabberID,
  Resource: WideString): Integer;
var
  c: TChatController;
begin
  c := MainSession.ChatList.FindChat(JabberID, Resource, '');
  if (c = nil) then
    Result := 0
  else
    Result := TForm(c.window).Handle;
end;

{---------------------------------------}

procedure TAnyQController.startInstantMsg(const JabberID: WideString);
begin
  startMsg(JabberID);
end;

{---------------------------------------}

procedure TAnyQController.startRoom(const RoomJID, Nickname,
  Password: WideString);
begin
  startRoom(RoomJID, Nickname, Password);
end;

{---------------------------------------}

procedure TAnyQController.startSearch(const SearchJID: WideString);
begin
  startSearch(SearchJID);
end;

{---------------------------------------}

procedure TAnyQController.showJoinRoom(const RoomJID, Nickname,
  Password: WideString);
var
  tmpjid: TJabberID;
begin
  tmpjid := TJabberID.Create(RoomJID);
  startJoinRoom(tmpjid, NickName, Password);
  tmpjid.free();
end;

{---------------------------------------}

procedure TAnyQController.startBrowser(const BrowseJID: WideString);
begin
  showBrowser(BrowseJID);
end;

{---------------------------------------}

procedure TAnyQController.showCustomPresDialog;
begin
  ShowCustomPresence();
end;

{---------------------------------------}

procedure TAnyQController.showDebug;
begin
  ShowDebugForm();
end;

{---------------------------------------}

procedure TAnyQController.showLogin;
begin
  PostMessage(frmAnyQ.Handle, WM_SHOWLOGIN, 0, 0);
end;

{---------------------------------------}

procedure TAnyQController.showPrefs;
begin
  StartOptions();
end;

{---------------------------------------}

procedure TAnyQController.showToast(const Message: WideString; wndHandle,
  imageIndex: Integer);
begin
  showRiserWindow(wndHandle, Message, imageIndex);
end;

{---------------------------------------}

procedure TAnyQController.setPresence(const Show, Status: WideString;
  Priority: Integer);
begin
  MainSession.setPresence(Show, Status, Priority);
end;

{---------------------------------------}

function TAnyQController.Get_Roster: IAnyQRoster;
begin
  ExCOMRoster.ObjAddRef();
  Result := ExCOMRoster;
end;

{---------------------------------------}

function TAnyQController.Get_PPDB: IAnyQPPDB;
begin
  ExCOMPPDB.ObjAddRef();
  Result := ExCOMPPDB;
end;

{---------------------------------------}

function TAnyQController.registerBrowseNS(
  const Namespace: WideString): WideString;
begin
  Result := IntToStr(AnyQ_Browse.Namespaces.Add(namespace));
end;

{---------------------------------------}

function TAnyQController.registerDiscoFeature(
  const Feature: WideString): WideString;
begin
  Result := IntToStr(AnyQ_Disco_Info.Features.Add(Feature));
end;

{---------------------------------------}

function TAnyQController.registerDiscoItem(const JabberID,
  Name: WideString): WideString;
begin
  Result := AnyQ_Disco_Items.addItem(Name, JabberID);
end;

{---------------------------------------}

procedure TAnyQController.removeBrowseNS(const ID: WideString);
var
  idx: integer;
begin
  idx := StrToIntDef(ID, -1);
  if ((idx >= 0) and (idx < AnyQ_Browse.Namespaces.Count)) then
    AnyQ_Browse.Namespaces.Delete(idx);
end;

{---------------------------------------}

procedure TAnyQController.removeDiscoFeature(const ID: WideString);
var
  idx: integer;
begin
  idx := StrToIntDef(ID, -1);
  if ((idx >= 0) and (idx < AnyQ_Disco_Info.Features.Count)) then
    AnyQ_Disco_Info.Features.Delete(idx);
end;

{---------------------------------------}

procedure TAnyQController.removeDiscoItem(const ID: WideString);
begin
  AnyQ_Disco_Items.removeItem(ID);
end;

{---------------------------------------}

function TAnyQController.registerPresenceXML(
  const XML: WideString): WideString;
begin
  Result := IntToStr(MainSession.Presence_XML.Add(XML));
end;

{---------------------------------------}

procedure TAnyQController.removePresenceXML(const ID: WideString);
var
  idx: integer;
begin
  idx := StrToIntDef(ID, -1);
  if ((idx >= 0) and (idx < MainSession.Presence_XML.Count)) then
    MainSession.Presence_XML.Delete(idx);
end;

{---------------------------------------}

procedure TAnyQController.trackWindowsMsg(Message: Integer);
begin
  frmAnyQ.TrackWindowsMsg(Message);
end;

{---------------------------------------}

function TAnyQController.addContactMenu(
  const Caption: WideString): WideString;
var
  id: Widestring;
  mi: TMenuItem;
begin
    // add a new TMenuItem to the Plugins menu
  mi := TMenuItem.Create(frmRosterWindow);
  frmRosterWindow.popRoster.Items.Add(mi);
  mi.Caption := caption;
  mi.OnClick := frmRosterWindow.pluginClick;
  id := 'ct_menu_' + IntToStr(_roster_menus.Count);
  mi.Name := id;
  _roster_menus.AddObject(id, mi);
  Result := id;
end;

{---------------------------------------}

procedure TAnyQController.removeContactMenu(const ID: WideString);
var
  idx: integer;
begin
  idx := _roster_menus.IndexOf(ID);
  if (idx >= 0) then begin
    TMenuItem(_roster_menus.Objects[idx]).Free();
    _roster_menus.Delete(idx);
  end;
end;

{---------------------------------------}

function TAnyQController.getActiveContact: WideString;
var
  ritem: TJabberRosterItem;
begin
  ritem := frmRosterWindow.CurRosterItem;
  if (ritem <> nil) then
    Result := ritem.jid.full
  else
    Result := '';
end;

{---------------------------------------}

function TAnyQController.getActiveGroup: WideString;
begin
  Result := frmRosterWindow.CurGroup;
end;

{---------------------------------------}

function TAnyQController.getActiveContacts(Online: WordBool): WideString;
var
  clist: TList;
  i: integer;
  ritem: TJabberRosterItem;
begin
    // send back: jid1 | jid2 | jid3 ...
  Result := '';
  clist := frmRosterWindow.getSelectedContacts(Online);

  for i := 0 to clist.count - 1 do begin
    ritem := TJabberRosterItem(clist[i]);
    if (ritem <> nil) then begin
      Result := Result + ritem.jid.full;
      if (i < clist.count - 1) then
        Result := Result + ' | ';
    end;
  end;
  clist.Clear();
  clist.Free();
end;

{---------------------------------------}

function TAnyQController.Get_LocalIP: WideString;
begin
  Result := MainSession.Stream.LocalIP;
end;

{---------------------------------------}

procedure TAnyQController.setPluginAuth(const AuthAgent: IAnyQAuth);
var
  aa: TExPluginAuth;
begin
    // spin up a new auth agent, and register it w/ the session.
  aa := TExPluginAuth.Create();
  aa.plugin := AuthAgent;
  MainSession.setAuthAgent(aa);
end;

{---------------------------------------}

procedure TAnyQController.setAuthenticated(Authed: WordBool;
  const XML: WideString);
begin
    // xxx: parse & pass along the XML Tag.
  MainSession.setAuthenticated(authed, nil);
end;

{---------------------------------------}

procedure TAnyQController.setAuthJID(const Username, Host,
  Resource: WideString);
begin
  MainSession.setAuthdJID(username, host, resource);
end;

{---------------------------------------}

function TAnyQController.addMessageMenu(
  const Caption: WideString): WideString;
var
  mc: TMenuContainer;
  id: Widestring;
begin
    // add a new TMenuItem to the Msg-Plugins menu
  id := 'msg_menu_' + IntToStr(_msg_menus.Count);
  mc := TMenuContainer.Create();
  mc.id := id;
  mc.caption := Caption;
  mc.idx := _msg_menus.AddObject(id, mc);
  Result := id;
end;

{---------------------------------------}

procedure TAnyQController.populateMsgMenus(parent: TPopupMenu;
  event: TNotifyEvent);
var
  i: integer;
  mc: TMenuContainer;
  mi: TMenuItem;
begin
  for i := 0 to _msg_menus.Count - 1 do begin
    mc := TMenuContainer(_msg_menus.Objects[i]);
    mi := TMenuItem.Create(parent.Owner);
    mi.Caption := mc.caption;
    mi.Tag := i;
    mi.OnClick := event;
    mi.Name := mc.id;
  end;
end;

procedure TAnyQController.SkinForm(aHandle: Integer);
begin
  frmAnyQ.SkinData1.SkinForm(aHandle);
end;

initialization
  TAutoObjectFactory.Create(ComServer, TAnyQController, Class_AnyQController,
    ciMultiInstance, tmApartment);

  plugs := TStringList.Create();
  proxies := TStringList.Create();

finalization
  plugs.Free();
  proxies.Free();

end.
