PageRenderTime 60ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/ecallmgr/src/freeswitch.erl

http://github.com/2600hz/whistle
Erlang | 387 lines | 239 code | 37 blank | 111 comment | 0 complexity | 20dc5927dcfa3f2000f0cd26c34129a0 MD5 | raw file
Possible License(s): GPL-3.0, MPL-2.0-no-copyleft-exception, Apache-2.0, JSON, BSD-3-Clause, Unlicense, LGPL-3.0, MIT
  1. %% The contents of this file are subject to the Mozilla Public License
  2. %% Version 1.1 (the "License"); you may not use this file except in
  3. %% compliance with the License. You may obtain a copy of the License at
  4. %% http://www.mozilla.org/MPL/
  5. %%
  6. %% Software distributed under the License is distributed on an "AS IS"
  7. %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
  8. %% License for the specific language governing rights and limitations
  9. %% under the License.
  10. %%
  11. %% @author Andrew Thompson <andrew AT hijacked DOT us>
  12. %% @copyright 2008-2009 Andrew Thompson
  13. %% @doc A module for interfacing with FreeSWITCH using mod_erlang_event.
  14. -module(freeswitch).
  15. -export([send/2, api/4, api/3, api/2, bgapi/3, bgapi/4, event/2, session_event/2,
  16. nixevent/2, session_nixevent/2, noevents/1, session_noevents/1, close/1,
  17. get_event_header/2, get_event_body/1,
  18. get_event_name/1, getpid/1, sendmsg/3,
  19. sendevent/3, sendevent_custom/3, handlecall/2, handlecall/3, start_fetch_handler/5,
  20. start_log_handler/4, start_event_handler/4, fetch_reply/3]).
  21. -define(TIMEOUT, 5000).
  22. %% @doc Return the value for a specific header in an event or `{error,notfound}'.
  23. get_event_header([], _Needle) ->
  24. {error, notfound};
  25. get_event_header({event, Headers}, Needle) when is_list(Headers) ->
  26. get_event_header(Headers, Needle);
  27. get_event_header([undefined | Headers], Needle) ->
  28. get_event_header(Headers, Needle);
  29. get_event_header([UUID | Headers], Needle) when is_list(UUID) ->
  30. get_event_header(Headers, Needle);
  31. get_event_header([{Key,Value} | Headers], Needle) ->
  32. case Key of
  33. Needle ->
  34. Value;
  35. _ ->
  36. get_event_header(Headers, Needle)
  37. end.
  38. %% @doc Return the name of the event.
  39. get_event_name(Event) ->
  40. get_event_header(Event, "Event-Name").
  41. %% @doc Return the body of the event or `{error, notfound}' if no event body.
  42. get_event_body(Event) ->
  43. get_event_header(Event, "body").
  44. %% @doc Send a raw term to FreeSWITCH. Returns the reply or `timeout' on a
  45. %% timeout.
  46. send(Node, Term) ->
  47. {send, Node} ! Term,
  48. receive
  49. Response ->
  50. Response
  51. after ?TIMEOUT ->
  52. timeout
  53. end.
  54. fetch_reply(Node, FetchID, Reply) ->
  55. {send, Node} ! {fetch_reply, FetchID, Reply},
  56. receive
  57. {ok, FetchID} ->
  58. ok;
  59. {error, FetchID, Reason} ->
  60. {error, Reason}
  61. after ?TIMEOUT ->
  62. timeout
  63. end.
  64. %% @doc Make a blocking API call to FreeSWITCH. The result of the API call is
  65. %% returned or `timeout' if FreeSWITCH fails to respond.
  66. api(Node, Cmd, Args) ->
  67. api(Node, Cmd, Args, ?TIMEOUT).
  68. api(Node, Cmd, Args, Timeout) ->
  69. {api, Node} ! {api, Cmd, Args},
  70. receive
  71. {ok, X} ->
  72. {ok, X};
  73. {error, X} ->
  74. {error, X}
  75. after Timeout ->
  76. timeout
  77. end.
  78. %% @doc Same as @link{api/3} except there's no additional arguments.
  79. api(Node, Cmd) ->
  80. api(Node, Cmd, "").
  81. %% @doc Make a backgrounded API call to FreeSWITCH. The asynchronous reply is
  82. %% sent to calling process after it is received. This function
  83. %% returns the result of the initial bgapi call or `timeout' if FreeSWITCH fails
  84. %% to respond.
  85. -spec(bgapi/3 :: (Node :: atom(), Cmd :: atom(), Args :: string()) -> {'ok', string()} | {'error', any()} | 'timeout').
  86. bgapi(Node, Cmd, Args) ->
  87. Self = self(),
  88. % spawn a new process so that both responses go here instead of directly to
  89. % the calling process.
  90. spawn(fun() ->
  91. {bgapi, Node} ! {bgapi, Cmd, Args},
  92. receive
  93. {error, Reason} ->
  94. % send the error condition to the calling process
  95. Self ! {api, {error, Reason}};
  96. {ok, JobID} ->
  97. % send the reply to the calling process
  98. Self ! {api, {ok, JobID}},
  99. receive % wait for the job's reply
  100. {bgok, JobID, Reply} ->
  101. % send the actual command output back to the calling process
  102. Self ! {bgok, JobID, Reply};
  103. {bgerror, JobID, Reply} ->
  104. Self ! {bgerror, JobID, Reply}
  105. end
  106. after ?TIMEOUT ->
  107. % send a timeout to the calling process
  108. Self ! {api, timeout}
  109. end
  110. end),
  111. % get the initial result of the command, NOT the asynchronous response, and
  112. % return it
  113. receive
  114. {api, X} -> X
  115. end.
  116. %% @doc Make a backgrounded API call to FreeSWITCH. The asynchronous reply is
  117. %% passed as the argument to `Fun' after it is received. This function
  118. %% returns the result of the initial bgapi call or `timeout' if FreeSWITCH fails
  119. %% to respond.
  120. -spec(bgapi/4 :: (Node :: atom(), Cmd :: atom(), Args :: string(), Fun :: fun()) -> 'ok' | {'error', any()} | 'timeout').
  121. bgapi(Node, Cmd, Args, Fun) ->
  122. Self = self(),
  123. % spawn a new process so that both responses go here instead of directly to
  124. % the calling process.
  125. spawn(fun() ->
  126. {bgapi, Node} ! {bgapi, Cmd, Args},
  127. receive
  128. {error, Reason} ->
  129. % send the error condition to the calling process
  130. Self ! {api, {error, Reason}};
  131. {ok, JobID} ->
  132. % send the reply to the calling process
  133. Self ! {api, ok},
  134. receive % wait for the job's reply
  135. {bgok, JobID, Reply} ->
  136. % Call the function with the reply
  137. Fun(ok, Reply);
  138. {bgerror, JobID, Reply} ->
  139. Fun(error, Reply)
  140. end
  141. after ?TIMEOUT ->
  142. % send a timeout to the calling process
  143. Self ! {api, timeout}
  144. end
  145. end),
  146. % get the initial result of the command, NOT the asynchronous response, and
  147. % return it
  148. receive
  149. {api, X} -> X
  150. end.
  151. %% @doc Request to receive any events in the list `List'.
  152. event(Node, Events) when is_list(Events) ->
  153. {event, Node} ! list_to_tuple(lists:append([event], Events)),
  154. receive
  155. ok -> ok;
  156. {error, Reason} -> {error, Reason}
  157. after ?TIMEOUT ->
  158. timeout
  159. end;
  160. event(Node, Event) when is_atom(Event) ->
  161. event(Node, [Event]).
  162. session_event(Node, Events) when is_list(Events) ->
  163. {session_event, Node} ! list_to_tuple([session_event | Events]),
  164. receive
  165. ok -> ok;
  166. {error, Reason} -> {error, Reason}
  167. after ?TIMEOUT ->
  168. timeout
  169. end;
  170. session_event(Node, Event) when is_atom(Event) ->
  171. session_event(Node, [Event]).
  172. %% @doc Stop receiving any events in the list `Events' from `Node'.
  173. nixevent(Node, Events) when is_list(Events) ->
  174. {nixevent, Node} ! list_to_tuple(lists:append([nixevent], Events)),
  175. receive
  176. ok -> ok;
  177. {error, Reason} -> {error, Reason}
  178. after ?TIMEOUT ->
  179. timeout
  180. end;
  181. nixevent(Node, Event) when is_atom(Event) ->
  182. nixevent(Node, [Event]).
  183. session_nixevent(Node, Events) when is_list(Events) ->
  184. {session_nixevent, Node} ! list_to_tuple([session_nixevent | Events]),
  185. receive
  186. ok -> ok;
  187. {error, Reason} -> {error, Reason}
  188. after ?TIMEOUT ->
  189. timeout
  190. end;
  191. session_nixevent(Node, Event) when is_atom(Event) ->
  192. session_nixevent(Node, [Event]).
  193. %% @doc Stop receiving any events from `Node'.
  194. noevents(Node) ->
  195. {noevents, Node} ! noevents,
  196. receive
  197. ok -> ok;
  198. {error, Reason} -> {error, Reason}
  199. after ?TIMEOUT ->
  200. timeout
  201. end.
  202. session_noevents(Node) ->
  203. {session_noevents, Node} ! session_noevents,
  204. receive
  205. ok -> ok;
  206. {error, Reason} -> {error, Reason}
  207. after ?TIMEOUT ->
  208. timeout
  209. end.
  210. %% @doc Close the connection to `Node'.
  211. close(Node) ->
  212. {close, Node} ! exit,
  213. receive
  214. ok -> ok
  215. after ?TIMEOUT ->
  216. timeout
  217. end.
  218. %% @doc Send an event to FreeSWITCH. `EventName' is the name of the event and
  219. %% `Headers' is a list of `{Key, Value}' string tuples. See the mod_event_socket
  220. %% documentation for more information.
  221. sendevent(Node, EventName, Headers) ->
  222. {sendevent, Node} ! {sendevent, EventName, Headers},
  223. receive
  224. ok -> ok;
  225. {error, Reason} -> {error, Reason}
  226. after ?TIMEOUT ->
  227. timeout
  228. end.
  229. %% @doc Send a CUSTOM event to FreeSWITCH. `SubClassName' is the name of the event
  230. %% subclass and `Headers' is a list of `{Key, Value}' string tuples. See the
  231. %% mod_event_socket documentation for more information.
  232. sendevent_custom(Node, SubClassName, Headers) ->
  233. {sendevent, Node} ! {sendevent, 'CUSTOM', SubClassName, Headers},
  234. receive
  235. ok -> ok;
  236. {error, Reason} -> {error, Reason}
  237. after ?TIMEOUT ->
  238. timeout
  239. end.
  240. %% @doc Send a message to the call identified by `UUID'. `Headers' is a list of
  241. %% `{Key, Value}' string tuples.
  242. sendmsg(Node, UUID, Headers) ->
  243. {sendmsg, Node} ! {sendmsg, UUID, Headers},
  244. receive
  245. ok -> ok;
  246. {error, Reason} -> {error, Reason}
  247. after ?TIMEOUT ->
  248. timeout
  249. end.
  250. %% @doc Get the fake pid of the FreeSWITCH node at `Node'. This can be helpful
  251. %% for linking to the process. Returns `{ok, Pid}' or `timeout'.
  252. getpid(Node) ->
  253. {getpid, Node} ! getpid,
  254. receive
  255. {ok, Pid} when is_pid(Pid) -> {ok, Pid}
  256. after ?TIMEOUT ->
  257. timeout
  258. end.
  259. %% @doc Request that FreeSWITCH send any events pertaining to call `UUID' to
  260. %% `Process' where process is a registered process name.
  261. handlecall(Node, UUID, Process) ->
  262. {handlecall, Node} ! {handlecall, UUID, Process},
  263. receive
  264. ok -> ok;
  265. {error, Reason} -> {error, Reason}
  266. after ?TIMEOUT ->
  267. timeout
  268. end.
  269. %% @doc Request that FreeSWITCH send any events pertaining to call `UUID' to
  270. %% the calling process.
  271. handlecall(Node, UUID) ->
  272. {handlecall, Node} ! {handlecall, UUID},
  273. receive
  274. ok -> ok;
  275. {error, Reason} -> {error, Reason}
  276. after ?TIMEOUT ->
  277. timeout
  278. end.
  279. %% @private
  280. start_handler(Node, Type, Module, Function, State) ->
  281. Self = self(),
  282. spawn(fun() ->
  283. monitor_node(Node, true),
  284. {foo, Node} ! Type,
  285. receive
  286. ok ->
  287. Self ! {Type, {ok, self()}},
  288. apply(Module, Function, [Node, State]);
  289. {error,Reason} ->
  290. Self ! {Type, {error, Reason}}
  291. after ?TIMEOUT ->
  292. Self ! {Type, timeout}
  293. end
  294. end),
  295. receive
  296. {Type, X} -> X
  297. end.
  298. %% @todo Notify the process if it gets replaced by a new log handler.
  299. %% @doc Spawn `Module':`Function' as a log handler. The process will receive
  300. %% messages of the form `{log, [{level, LogLevel}, {text_channel, TextChannel}, {file, FileName}, {func, FunctionName}, {line, LineNumber}, {data, LogMessage}]}'
  301. %% or `{nodedown, Node}' if the FreesSWITCH node at `Node' exits.
  302. %%
  303. %% The function specified by `Module':`Function' should be tail recursive and is
  304. %% passed one argument; the name of the FreeSWITCH node.
  305. %%
  306. %% Subsequent calls to this function for the same node replaces the
  307. %% previous event handler with the newly spawned one.
  308. %%
  309. %% This function returns either `{ok, Pid}' where `Pid' is the pid of the newly
  310. %% spawned process, `{error, Reason}' or the atom `timeout' if FreeSWITCH did
  311. %% not respond.
  312. start_log_handler(Node, Module, Function, State) ->
  313. start_handler(Node, register_log_handler, Module, Function, State).
  314. %% @todo Notify the process if it gets replaced with a new event handler.
  315. %% @doc Spawn Module:Function as an event handler. The process will receive
  316. %% messages of the form `{event, [UniqueID, {Key, Value}, {...}]}' where
  317. %% `UniqueID' is either a FreeSWITCH call ID or `undefined' or
  318. %% `{nodedown, Node}' if the FreeSWITCH node at `Node' exits.
  319. %%
  320. %% The function specified by `Module':`Function' should be tail recursive and is
  321. %% passed one argument; the name of the FreeSWITCH node.
  322. %%
  323. %% Subsequent calls to this function for the same node replaces the
  324. %% previous event handler with the newly spawned one.
  325. %%
  326. %% This function returns either `{ok, Pid}' where `Pid' is the pid of the newly
  327. %% spawned process, `{error, Reason}' or the atom `timeout' if FreeSWITCH did
  328. %% not respond.
  329. start_event_handler(Node, Module, Function, State) ->
  330. start_handler(Node, register_event_handler, Module, Function, State).
  331. %% @doc Spawn Module:Function as an XML config fetch handler for configs of type
  332. %% `Section'. See the FreeSWITCH documentation for mod_xml_rpc for more
  333. %% information on sections. The process will receive messages of the form
  334. %% `{fetch, Section, Tag, Key, Value, ID, Data}' or `{nodedown, Node}' if the
  335. %% FreeSWITCH node at `Node' exits.
  336. %%
  337. %% The function specified by `Module':`Function' should be tail recursive and is
  338. %% passed one argument; the name of the FreeSWITCH node. The function should
  339. %% send tuples back to FreeSWITCH of the form `{fetch_reply, ID, XML}' where
  340. %%`ID' is the ID received in the request tuple and `XML' is XML in string or
  341. %% binary form of the form noted in the mod_xml_rpc documentation.
  342. %%
  343. %% Subsequent calls to this function for the same node and section will yield
  344. %% undefined behaviour.
  345. %%
  346. %% This function returns either `{ok, Pid}' where `Pid' is the pid of the newly
  347. %% spawned process, `{error, Reason}' or the atom `timeout' if FreeSWITCH did
  348. %% not respond.
  349. start_fetch_handler(Node, Section, Module, Function, State) ->
  350. start_handler(Node, {bind, Section}, Module, Function, State).