mirror of
https://github.com/signalwire/freeswitch.git
synced 2025-02-24 10:31:13 +00:00
git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@14362 d0543943-73ff-0310-b7d9-9358b9ac24b2
342 lines
11 KiB
Erlang
342 lines
11 KiB
Erlang
%% The contents of this file are subject to the Mozilla Public License
|
|
%% Version 1.1 (the "License"); you may not use this file except in
|
|
%% compliance with the License. You may obtain a copy of the License at
|
|
%% http://www.mozilla.org/MPL/
|
|
%%
|
|
%% Software distributed under the License is distributed on an "AS IS"
|
|
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
|
|
%% License for the specific language governing rights and limitations
|
|
%% under the License.
|
|
%%
|
|
%% @author Andrew Thompson <andrew AT hijacked DOT us>
|
|
%% @copyright 2008-2009 Andrew Thompson
|
|
%% @doc A module for interfacing with FreeSWITCH using mod_erlang_event.
|
|
|
|
-module(freeswitch).
|
|
|
|
-export([send/2, api/3, api/2, bgapi/3, bgapi/4, event/2,
|
|
nixevent/2, noevents/1, close/1,
|
|
get_event_header/2, get_event_body/1,
|
|
get_event_name/1, getpid/1, sendmsg/3,
|
|
sendevent/3, sendevent_custom/3, handlecall/2, handlecall/3, start_fetch_handler/5,
|
|
start_log_handler/4, start_event_handler/4]).
|
|
-define(TIMEOUT, 5000).
|
|
|
|
%% @doc Return the value for a specific header in an event or `{error,notfound}'.
|
|
get_event_header([], _Needle) ->
|
|
{error, notfound};
|
|
get_event_header({event, Headers}, Needle) when is_list(Headers) ->
|
|
get_event_header(Headers, Needle);
|
|
get_event_header([undefined | Headers], Needle) ->
|
|
get_event_header(Headers, Needle);
|
|
get_event_header([UUID | Headers], Needle) when is_list(UUID) ->
|
|
get_event_header(Headers, Needle);
|
|
get_event_header([{Key,Value} | Headers], Needle) ->
|
|
case Key of
|
|
Needle ->
|
|
Value;
|
|
_ ->
|
|
get_event_header(Headers, Needle)
|
|
end.
|
|
|
|
%% @doc Return the name of the event.
|
|
get_event_name(Event) ->
|
|
get_event_header(Event, "Event-Name").
|
|
|
|
%% @doc Return the body of the event or `{error, notfound}' if no event body.
|
|
get_event_body(Event) ->
|
|
get_event_header(Event, "body").
|
|
|
|
%% @doc Send a raw term to FreeSWITCH. Returns the reply or `timeout' on a
|
|
%% timeout.
|
|
send(Node, Term) ->
|
|
{send, Node} ! Term,
|
|
receive
|
|
Response ->
|
|
Response
|
|
after ?TIMEOUT ->
|
|
timeout
|
|
end.
|
|
|
|
%% @doc Make a blocking API call to FreeSWITCH. The result of the API call is
|
|
%% returned or `timeout' if FreeSWITCH fails to respond.
|
|
api(Node, Cmd, Args) ->
|
|
{api, Node} ! {api, Cmd, Args},
|
|
receive
|
|
{ok, X} ->
|
|
{ok, X};
|
|
{error, X} ->
|
|
{error, X}
|
|
after ?TIMEOUT ->
|
|
timeout
|
|
end.
|
|
|
|
%% @doc Same as @link{api/3} except there's no additional arguments.
|
|
api(Node, Cmd) ->
|
|
api(Node, Cmd, "").
|
|
|
|
%% @doc Make a backgrounded API call to FreeSWITCH. The asynchronous reply is
|
|
%% sent to calling process after it is received. This function
|
|
%% returns the result of the initial bgapi call or `timeout' if FreeSWITCH fails
|
|
%% to respond.
|
|
-spec(bgapi/3 :: (Node :: atom(), Cmd :: atom(), Args :: string()) -> {'ok', string()} | {'error', any()} | 'timeout').
|
|
bgapi(Node, Cmd, Args) ->
|
|
Self = self(),
|
|
% spawn a new process so that both responses go here instead of directly to
|
|
% the calling process.
|
|
spawn(fun() ->
|
|
{bgapi, Node} ! {bgapi, Cmd, Args},
|
|
receive
|
|
{error, Reason} ->
|
|
% send the error condition to the calling process
|
|
Self ! {api, {error, Reason}};
|
|
{ok, JobID} ->
|
|
% send the reply to the calling process
|
|
Self ! {api, {ok, JobID}},
|
|
receive % wait for the job's reply
|
|
{bgok, JobID, Reply} ->
|
|
% send the actual command output back to the calling process
|
|
Self ! {bgok, JobID, Reply};
|
|
{bgerror, JobID, Reply} ->
|
|
Self ! {bgerror, JobID, Reply}
|
|
end
|
|
after ?TIMEOUT ->
|
|
% send a timeout to the calling process
|
|
Self ! {api, timeout}
|
|
end
|
|
end),
|
|
|
|
% get the initial result of the command, NOT the asynchronous response, and
|
|
% return it
|
|
receive
|
|
{api, X} -> X
|
|
end.
|
|
|
|
%% @doc Make a backgrounded API call to FreeSWITCH. The asynchronous reply is
|
|
%% passed as the argument to `Fun' after it is received. This function
|
|
%% returns the result of the initial bgapi call or `timeout' if FreeSWITCH fails
|
|
%% to respond.
|
|
-spec(bgapi/4 :: (Node :: atom(), Cmd :: atom(), Args :: string(), Fun :: fun()) -> 'ok' | {'error', any()} | 'timeout').
|
|
bgapi(Node, Cmd, Args, Fun) ->
|
|
Self = self(),
|
|
% spawn a new process so that both responses go here instead of directly to
|
|
% the calling process.
|
|
spawn(fun() ->
|
|
{bgapi, Node} ! {bgapi, Cmd, Args},
|
|
receive
|
|
{error, Reason} ->
|
|
% send the error condition to the calling process
|
|
Self ! {api, {error, Reason}};
|
|
{ok, JobID} ->
|
|
% send the reply to the calling process
|
|
Self ! {api, ok},
|
|
receive % wait for the job's reply
|
|
{bgok, JobID, Reply} ->
|
|
% Call the function with the reply
|
|
Fun(ok, Reply);
|
|
{bgerror, JobID, Reply} ->
|
|
Fun(error, Reply)
|
|
end
|
|
after ?TIMEOUT ->
|
|
% send a timeout to the calling process
|
|
Self ! {api, timeout}
|
|
end
|
|
end),
|
|
|
|
% get the initial result of the command, NOT the asynchronous response, and
|
|
% return it
|
|
receive
|
|
{api, X} -> X
|
|
end.
|
|
|
|
%% @doc Request to receive any events in the list `List'.
|
|
event(Node, Events) when is_list(Events) ->
|
|
{event, Node} ! list_to_tuple(lists:append([event], Events)),
|
|
receive
|
|
ok -> ok;
|
|
{error, Reason} -> {error, Reason}
|
|
after ?TIMEOUT ->
|
|
timeout
|
|
end;
|
|
event(Node, Event) when is_atom(Event) ->
|
|
event(Node, [Event]).
|
|
|
|
%% @doc Stop receiving any events in the list `Events' from `Node'.
|
|
nixevent(Node, Events) when is_list(Events) ->
|
|
{nixevent, Node} ! list_to_tuple(lists:append([nixevent], Events)),
|
|
receive
|
|
X -> X
|
|
after ?TIMEOUT ->
|
|
timeout
|
|
end;
|
|
nixevent(Node, Event) when is_atom(Event) ->
|
|
nixevent(Node, [Event]).
|
|
|
|
%% @doc Stop receiving any events from `Node'.
|
|
noevents(Node) ->
|
|
{noevents, Node} ! noevents,
|
|
receive
|
|
ok -> ok;
|
|
{error, Reason} -> {error, Reason}
|
|
after ?TIMEOUT ->
|
|
timeout
|
|
end.
|
|
|
|
%% @doc Close the connection to `Node'.
|
|
close(Node) ->
|
|
{close, Node} ! exit,
|
|
receive
|
|
ok -> ok
|
|
after ?TIMEOUT ->
|
|
timeout
|
|
end.
|
|
|
|
%% @doc Send an event to FreeSWITCH. `EventName' is the name of the event and
|
|
%% `Headers' is a list of `{Key, Value}' string tuples. See the mod_event_socket
|
|
%% documentation for more information.
|
|
sendevent(Node, EventName, Headers) ->
|
|
{sendevent, Node} ! {sendevent, EventName, Headers},
|
|
receive
|
|
ok -> ok;
|
|
{error, Reason} -> {error, Reason}
|
|
after ?TIMEOUT ->
|
|
timeout
|
|
end.
|
|
|
|
%% @doc Send a CUSTOM event to FreeSWITCH. `SubClassName' is the name of the event
|
|
%% subclass and `Headers' is a list of `{Key, Value}' string tuples. See the
|
|
%% mod_event_socket documentation for more information.
|
|
sendevent_custom(Node, SubClassName, Headers) ->
|
|
{sendevent, Node} ! {sendevent, 'CUSTOM', SubClassName, Headers},
|
|
receive
|
|
ok -> ok;
|
|
{error, Reason} -> {error, Reason}
|
|
after ?TIMEOUT ->
|
|
timeout
|
|
end.
|
|
|
|
|
|
%% @doc Send a message to the call identified by `UUID'. `Headers' is a list of
|
|
%% `{Key, Value}' string tuples.
|
|
sendmsg(Node, UUID, Headers) ->
|
|
{sendmsg, Node} ! {sendmsg, UUID, Headers},
|
|
receive
|
|
ok -> ok;
|
|
{error, Reason} -> {error, Reason}
|
|
after ?TIMEOUT ->
|
|
timeout
|
|
end.
|
|
|
|
|
|
%% @doc Get the fake pid of the FreeSWITCH node at `Node'. This can be helpful
|
|
%% for linking to the process. Returns `{ok, Pid}' or `timeout'.
|
|
getpid(Node) ->
|
|
{getpid, Node} ! getpid,
|
|
receive
|
|
{ok, Pid} when is_pid(Pid) -> {ok, Pid}
|
|
after ?TIMEOUT ->
|
|
timeout
|
|
end.
|
|
|
|
%% @doc Request that FreeSWITCH send any events pertaining to call `UUID' to
|
|
%% `Process' where process is a registered process name.
|
|
handlecall(Node, UUID, Process) ->
|
|
{handlecall, Node} ! {handlecall, UUID, Process},
|
|
receive
|
|
ok -> ok;
|
|
{error, Reason} -> {error, Reason}
|
|
after ?TIMEOUT ->
|
|
timeout
|
|
end.
|
|
|
|
%% @doc Request that FreeSWITCH send any events pertaining to call `UUID' to
|
|
%% the calling process.
|
|
handlecall(Node, UUID) ->
|
|
{handlecall, Node} ! {handlecall, UUID},
|
|
receive
|
|
ok -> ok;
|
|
{error, Reason} -> {error, Reason}
|
|
after ?TIMEOUT ->
|
|
timeout
|
|
end.
|
|
|
|
%% @private
|
|
start_handler(Node, Type, Module, Function, State) ->
|
|
Self = self(),
|
|
spawn(fun() ->
|
|
monitor_node(Node, true),
|
|
{foo, Node} ! Type,
|
|
receive
|
|
ok ->
|
|
io:format("OK!!!!!!!~n"),
|
|
Self ! {Type, {ok, self()}},
|
|
apply(Module, Function, [Node, State]);
|
|
{error,Reason} ->
|
|
Self ! {Type, {error, Reason}}
|
|
after ?TIMEOUT ->
|
|
Self ! {Type, timeout}
|
|
end
|
|
end),
|
|
|
|
receive
|
|
{Type, X} -> X
|
|
end.
|
|
|
|
%% @todo Notify the process if it gets replaced by a new log handler.
|
|
|
|
%% @doc Spawn `Module':`Function' as a log handler. The process will receive
|
|
%% messages of the form `{log, [{level, LogLevel}, {text_channel, TextChannel}, {file, FileName}, {func, FunctionName}, {line, LineNumber}, {data, LogMessage}]}'
|
|
%% or `{nodedown, Node}' if the FreesSWITCH node at `Node' exits.
|
|
%%
|
|
%% The function specified by `Module':`Function' should be tail recursive and is
|
|
%% passed one argument; the name of the FreeSWITCH node.
|
|
%%
|
|
%% Subsequent calls to this function for the same node replaces the
|
|
%% previous event handler with the newly spawned one.
|
|
%%
|
|
%% This function returns either `{ok, Pid}' where `Pid' is the pid of the newly
|
|
%% spawned process, `{error, Reason}' or the atom `timeout' if FreeSWITCH did
|
|
%% not respond.
|
|
start_log_handler(Node, Module, Function, State) ->
|
|
start_handler(Node, register_log_handler, Module, Function, State).
|
|
|
|
%% @todo Notify the process if it gets replaced with a new event handler.
|
|
|
|
%% @doc Spawn Module:Function as an event handler. The process will receive
|
|
%% messages of the form `{event, [UniqueID, {Key, Value}, {...}]}' where
|
|
%% `UniqueID' is either a FreeSWITCH call ID or `undefined' or
|
|
%% `{nodedown, Node}' if the FreeSWITCH node at `Node' exits.
|
|
%%
|
|
%% The function specified by `Module':`Function' should be tail recursive and is
|
|
%% passed one argument; the name of the FreeSWITCH node.
|
|
%%
|
|
%% Subsequent calls to this function for the same node replaces the
|
|
%% previous event handler with the newly spawned one.
|
|
%%
|
|
%% This function returns either `{ok, Pid}' where `Pid' is the pid of the newly
|
|
%% spawned process, `{error, Reason}' or the atom `timeout' if FreeSWITCH did
|
|
%% not respond.
|
|
start_event_handler(Node, Module, Function, State) ->
|
|
start_handler(Node, register_event_handler, Module, Function, State).
|
|
|
|
%% @doc Spawn Module:Function as an XML config fetch handler for configs of type
|
|
%% `Section'. See the FreeSWITCH documentation for mod_xml_rpc for more
|
|
%% information on sections. The process will receive messages of the form
|
|
%% `{fetch, Section, Tag, Key, Value, ID, Data}' or `{nodedown, Node}' if the
|
|
%% FreeSWITCH node at `Node' exits.
|
|
%%
|
|
%% The function specified by `Module':`Function' should be tail recursive and is
|
|
%% passed one argument; the name of the FreeSWITCH node. The function should
|
|
%% send tuples back to FreeSWITCH of the form `{fetch_reply, ID, XML}' where
|
|
%%`ID' is the ID received in the request tuple and `XML' is XML in string or
|
|
%% binary form of the form noted in the mod_xml_rpc documentation.
|
|
%%
|
|
%% Subsequent calls to this function for the same node and section will yield
|
|
%% undefined behaviour.
|
|
%%
|
|
%% This function returns either `{ok, Pid}' where `Pid' is the pid of the newly
|
|
%% spawned process, `{error, Reason}' or the atom `timeout' if FreeSWITCH did
|
|
%% not respond.
|
|
start_fetch_handler(Node, Section, Module, Function, State) ->
|
|
start_handler(Node, {bind, Section}, Module, Function, State).
|