anchor.diff:  
     ok.
   
  -on_presence(User, Server, Resource, {xmlelement, "presence", _ , _} = Packet) ->
  -  {IPAddress, _} = ejabberd_sm:get_user_ip(User, Server, Resource),
  -  ?INFO_MSG("***Connected user ~p from ~p", [User, IPAddress]),
  -  gen_server:cast(?MODULE, {userconnect, User, IPAddress}),
  -  report_event(Server, "connect"),
  +on_set_presence(User, Server, Resource, _Packet) ->   handle_presence(connect, {User, Server, Resource}).
  +on_unset_presence(User, Server, Resource, _Status) -> handle_presence(disconnect, {User, Server, Resource}).
  +
  +%@doc Handles presence stanzas sent by users
  +handle_presence(UserState, {User, Server, Resource} = JID) ->
  +  {IPAddress, _} = ejabberd:get_user_ip(User, Server, Resource),
  +  ?DEBUG("User ~p (~p) is ~p", [JID, IPAddress, State]),
  +
  +  % Passes over the processing into a new process
  +  gen_server:cast(?MODULE, {UserState, JID, IPAddress}),
  +  report_event(Server, UserState),
     ok.
   
   %%% gen_server %%%
  
positive.code:                      [?MODULE]},
    supervisor:start_child(ejabberd_sup, ChildSpec),
  
    ejabberd_hooks:add(set_presence_hook, Host, ?MODULE, on_presence, 50),
    ok.
  
  stop(Host) ->
    ?INFO_MSG("** terminate copilot plugin", []),
  
    ejabberd_hooks:delete(set_presence_hook, Host, ?MODULE, on_presence),
  
    Proc = gen_mod:get_module_proc(Host, ?MODULE),
    supervisor:terminate_child(ejabberd_sup, Proc),
  
negative.code:  {ok, State}.
  
  %%% Utilities %%%
  %@doc Converts first character to uppercase
  ucfirst([]) -> [];
  ucfirst([First|Rest]) -> string:to_upper(lists:nth(1, io_lib:format("~c", [First]))) ++ Rest.
  
  %@doc Mongo driver invalidates the connection on every error,
  mongo_reconnect(State) ->
    mongo:disconnect(proplists:get_value(mongo, State)),
    Opts = proplists:get_value(opts, State),
    Host = case proplists:get_value(mongodb, Opts) of
      undefined -> {localhost, 27017};
      Value -> Value;
    end,
    {ok, NewConn} = mongo:connect(Host),
    proplists:delete(conn, State) ++ [{conn, NewConn}].
  
  %@doc Wrapper around mongo:do/5 which is too error-sensitive
  mongo_do(State, Table, Op) ->
    Conn = proplists:get_value(mongo, State),
    case mongo:do(safe, master, Conn, Table, Op) of
      {ok, _} -> {noreply, State},
      {failure, Failure} ->
        ?DEBUG("MongoDB operation failed with: ~p", [Failure]),
        {noreply, mongo_reconnect(State)}
    end.
  
  %@doc Returns geosplatial information for given IP
  lookup_ip(IPAddress) ->
    ?DEBUG("Decoding IP address ~p", [IPAddress]),
    [_|Data] = erlang:tuple_to_list(element(2, egeoip:lookup(IPAddress))),
    lists:zip(egeoip:record_fields(), Data).
  
  %@doc Parses JID (username part) of an agent (ex.: agent_s-10829_1_7_18225d4f-e3f7-4c18-9a18-d6c06992d272_-g)
  parse_component_jid(User) ->
    lists:zip([component, version, cores, jobs, uuid, ukn], strings:tokens(User, "_")).
  
  %@doc Creates a MonoDB document describing the connection
  doc_create_connection({User, Host, Resource} = JID, IPAddress) ->
    GeoData = lookup_ip(IPAddress),
    X = proplists:get_value(longitude, GeoData),
    Y = proplists:get_value(latitude, GeoData),
  
    {jid, User ++ "@" ++ Host ++ "/" ++ Resource,
     loc, [X, Y],
     active, true}
  
  %@doc Marks connection as closed and appends data from agent's JID
  doc_close_connection({ok, {}}, JID, IPAddress) ->
    % There isn't a connection document so we have to create it first
    doc_close_connection(doc_create_connection(JID, IPAddress));
  doc_close_connection({ok, {Doc}}, {User, _Host, _Resource} = JID, IPAddress) ->
    % Converts a list of tuples ([{x, Y}]) into a list of lists ([[x, Y]]) and flattens it ([x, Y]) for BSON
    AgentData = erlang:list_to_tuple(list:flatmap(fun erlang:tuple_to_list/1, parse_component_jid(User))),
    NewData = {disconnected_at, bson:timenow(),
               agent_data, AgentData},
    % Updates the active flag and appends new data
    bson:append(bson:update(active, false, Doc), NewData);
  doc_close_connection({failure, _}, _, _, _) ->
    % Silently fails
    failure.
  
  %@doc Sends an message as mod_copilot@localhost and performs base64-encoding of the attributes
  route_to_copilot_component(To, Xml) -> route_to_copilot_component(?JID, To, Xml).
  route_to_copilot_component(From, To, Xml) -> ejabberd_router:route(jlib:string_to_jid(From), jlib:string_to_jid(To), hash_xml_body(Xml)).
  
  %@doc Parses xmlelements and hashes attributes
  hash_xml_body({xmlelement, Tag, Attributes, ChildNodes}) ->
    {xmlelement,
     Tag,
     lists:map(fun hash_xml_attribute/1, Attributes),
     lists:map(fun hash_xml_body/1, ChildNodes)}.
  
  %@doc Performs base64-encoding on tuple's second element
  hash_xml_attribute({"to", _} = Attr) -> Attr;
  hash_xml_attribute({"from", _} = Attr) -> Attr;
  hash_xml_attribute({"noack", _} = Attr) -> Attr;
  hash_xml_attribute({Attr, Value}) -> {Attr, "_BASE64:" ++  base64:encode_to_string(Value)}.
  
  %@doc Prepares the reportEvent command
  prepare_event_command(Event) -> prepare_event_command(Event, "").
  prepare_event_command(Event, Type) -> lists:keydelete(Type, 1, prepare_event_command(Event, Type, "")).
  prepare_event_command(Event, Type, Value) ->
  