anchor.diff:  %%%===================================================================
   %%% @copyright (C) 2011, Erlang Solutions Ltd.
  -%%% @doc Module abstracting away various socket implementations
  +%%% @doc Module providing basic client functionality
   %%% @end
   %%%===================================================================
   -module(lxmppc).
  
positive.code: -include_lib("exml/include/exml_stream.hrl").
  -include("lxmppc.hrl").
  
  -export([connect/2,
           send/2,
           reset_stream/1,
           is_connected/1,
           stop/1]).
  
  %%%===================================================================
  %%% Public API
  %%%===================================================================
  
  connect(Type, Args) ->
      Mod = get_module(Type),
  
negative.code:     Mod:stop(Transport).
  
  %%%===================================================================
  %%% Client start implementation
  %%%===================================================================
  
  start_connect(Type, Host, Port, Props0) ->
      case connect(Type, {Host, Port}) of
          {ok, Conn} ->
              ok = send(Conn, lxmppc_stanza:stream_start(Host)),
              Props1 = wait_for_stream(Conn, Props0),
              Props2 = authenticate(Conn, Props1),
              Props3 = bind(Conn, Props2),
              Props4 = session(Conn, Props3),
              {ok, Conn, Props4};
          {error, Err} ->
              throw({failed_to_connect, Err})
      end.
  
  wait_for_stream(Conn, Props) ->
      StreamStart = get_stanza(Conn, wait_for_stream),
      %% FIXME: verify StreamStart
      StreamFeatures = get_stanza(Conn, wait_for_features),
      %% FIXME: verify StreamFeatures, add props
      Props.
  
  authenticate(Conn, Props) ->
      Username = proplists:get_value(username, Props, <<"anonymous">>),
      Password = proplists:get_value(password, Props, <<"">>),
      Host = proplists:get_value(host, Props),
      %% FIXME: select authentication scheme based on stream features
      ok = send(Conn, lxmppc_stanza:auth_plain(Username, Password)),
      AuthReply = get_stanza(Conn, auth_reply),
      case AuthReply#xmlelement.name of
          <<"success">> ->
              reset_stream(Conn),
              ok = send(Conn, lxmppc_stanza:stream_start(Host)),
              wait_for_stream(Conn, Props);
          <<"failure">> ->
              throw({auth_failed, AuthReply})
      end.
  
  bind(Conn, Props) ->
      Resource = proplists:get_value(resource, Props, <<"lxmppc-resource">>),
      send(Conn, lxmppc_stanza:bind(Resource)),
      BindReply = get_stanza(Conn, bind_reply),
      %% FIXME: verify BindReply, add props
      Props.
  
  session(Conn, Props) ->
      send(Conn, lxmppc_stanza:session()),
      SessionReply = get_stanza(Conn, session_reply),
      %% FIXME: verify SessionReply, add props
      Props.
  
  %%%===================================================================
  %%% Helpers
  %%%===================================================================
  
  get_stanza(Conn, Name) ->
      receive
          {stanza, Conn, Stanza} ->
              Stanza
      after ?TIMEOUT ->
              throw({timeout, Name})
      end.
  
  get_module(tcp) ->
      lxmppc_socket_tcp.
  