PageRenderTime 47ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/ucengine/src/models/uce_user.erl

http://github.com/AF83/ucengine
Erlang | 365 lines | 277 code | 43 blank | 45 comment | 2 complexity | 891d8de3a043c01ca2de43e3f4519158 MD5 | raw file
  1. %%
  2. %% U.C.Engine - Unified Collaboration Engine
  3. %% Copyright (C) 2011 af83
  4. %%
  5. %% This program is free software: you can redistribute it and/or modify
  6. %% it under the terms of the GNU Affero General Public License as published by
  7. %% the Free Software Foundation, either version 3 of the License, or
  8. %% (at your option) any later version.
  9. %%
  10. %% This program is distributed in the hope that it will be useful,
  11. %% but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. %% GNU Affero General Public License for more details.
  14. %%
  15. %% You should have received a copy of the GNU Affero General Public License
  16. %% along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. %%
  18. -module(uce_user).
  19. % public api
  20. -export([add/2,
  21. delete/2,
  22. update/2,
  23. list/1,
  24. get/2,
  25. get_by_name/2,
  26. exists/2,
  27. acl/3,
  28. add_role/3,
  29. delete_role/3,
  30. start_link/2]).
  31. % gen_server callbacks
  32. -export([init/1,
  33. code_change/3,
  34. handle_call/3,
  35. handle_cast/2,
  36. handle_info/2,
  37. terminate/2]).
  38. -behaviour(gen_server).
  39. -include("uce.hrl").
  40. %
  41. % Public api
  42. %
  43. -spec add(domain(), user()) -> {ok, uid()} | erlang:throw({error, conflict}).
  44. add(Domain, #uce_user{id=none} = User) ->
  45. add(Domain, User#uce_user{id=utils:random()});
  46. add(Domain, #uce_user{id=UId, name=Name} = User) ->
  47. case exists_by_name(Domain, Name) of
  48. true ->
  49. throw({error,conflict});
  50. false ->
  51. uce_role:add(Domain, #uce_role{id=UId}),
  52. DefaultRoles = [{"default", ""}, {UId, ""}],
  53. (db:get(?MODULE, Domain)):add(Domain,
  54. User#uce_user{roles=User#uce_user.roles ++ DefaultRoles}),
  55. {ok, UId}
  56. end.
  57. -spec delete(domain(), uid()) -> {ok, deleted} | erlang:throw({error, not_found}) | erlang:throw({error, any()}).
  58. delete(Domain, Uid) ->
  59. case exists(Domain, Uid) of
  60. true ->
  61. % delete the default role
  62. case catch uce_role:delete(Domain, Uid) of
  63. {error, Reason} when Reason /= not_found ->
  64. throw({error, Reason});
  65. {ok, deleted}->
  66. case get_pid_of(Domain, Uid) of
  67. {error, not_found} ->
  68. ok;
  69. {ok, Pid} ->
  70. ok = gen_server:call(Pid, stop)
  71. end,
  72. (db:get(?MODULE, Domain)):delete(Domain, Uid)
  73. end;
  74. false ->
  75. throw({error, not_found})
  76. end.
  77. -spec update(domain(), user()) -> {ok, updated} | erlang:throw({error, not_found}).
  78. update(Domain, #uce_user{id=Uid} = User) ->
  79. case exists(Domain, Uid) of
  80. true ->
  81. case get_pid_of(Domain, Uid) of
  82. {error, not_found} ->
  83. ok;
  84. {ok, Pid} ->
  85. gen_server:cast(Pid, {update_user, User})
  86. end,
  87. (db:get(?MODULE, Domain)):update(Domain, User);
  88. false ->
  89. throw({error, not_found})
  90. end.
  91. -spec list(domain()) -> {ok, list(user())}.
  92. list(Domain) ->
  93. (db:get(?MODULE, Domain)):list(Domain).
  94. -spec get(domain(), uid()) -> {ok, user()} | erlang:throw({error, not_found}).
  95. get(Domain, Uid) ->
  96. case get_pid_of(Domain, Uid) of
  97. {error, not_found} ->
  98. (db:get(?MODULE, Domain)):get(Domain, Uid);
  99. {ok, Pid} ->
  100. gen_server:call(Pid, get_user)
  101. end.
  102. -spec get_by_name(domain(), list()) -> {ok, user()} | erlang:throw({error, not_found}).
  103. get_by_name(Domain, Name) ->
  104. (db:get(?MODULE, Domain)):get_by_name(Domain, Name).
  105. -spec exists(domain(), uid()) -> true | false | erlang:throw({error, any()}).
  106. % "" value are used in uce_event:add
  107. % From or To can be empty
  108. exists(_Domain, "") ->
  109. true;
  110. exists(Domain, Uid) ->
  111. case get_pid_of(Domain, Uid) of
  112. {error, not_found} ->
  113. case catch get(Domain, Uid) of
  114. {error, not_found} ->
  115. false;
  116. {error, Reason} ->
  117. throw({error, Reason});
  118. {ok, _User}->
  119. true
  120. end;
  121. {ok, _Pid} ->
  122. true
  123. end.
  124. -spec add_role(domain(), uid(), {role(), meeting_id()}) -> {ok, updated} | erlang:throw({error, not_found}).
  125. add_role(Domain, Uid, {Role, Location}) ->
  126. % Just ensure the role and location exists
  127. case uce_meeting:exists(Domain, Location) of
  128. true ->
  129. case uce_role:exists(Domain, Role) of
  130. true ->
  131. {ok, User} = get(Domain, Uid),
  132. case lists:member({Role, Location}, User#uce_user.roles) of
  133. true ->
  134. {ok, updated};
  135. false ->
  136. update(Domain, User#uce_user{roles=(User#uce_user.roles ++ [{Role, Location}])})
  137. end;
  138. false ->
  139. throw({error, not_found})
  140. end;
  141. false ->
  142. throw({error, not_found})
  143. end.
  144. -spec delete_role(domain(), uid(), {role(), meeting_id()}) -> {ok, updated} | erlang:throw({error, not_found}).
  145. delete_role(Domain, Id, {Role, Location}) ->
  146. {ok, User} = get(Domain, Id),
  147. Roles = case lists:member({Role, Location}, User#uce_user.roles) of
  148. true ->
  149. lists:delete({Role, Location}, User#uce_user.roles);
  150. false ->
  151. throw({error, not_found})
  152. end,
  153. update(Domain, User#uce_user{roles=Roles}).
  154. -spec acl(domain(), uid(), list()) -> {ok, list()}.
  155. acl(Domain, User, Location) ->
  156. {ok, Record} = get(Domain, User),
  157. ACL = lists:map(fun({RoleName, RoleLocation}) ->
  158. {ok, RoleACL} =
  159. if
  160. RoleLocation == "" ->
  161. uce_role:acl(Domain, RoleName);
  162. RoleLocation == Location ->
  163. uce_role:acl(Domain, RoleName);
  164. true ->
  165. {ok, []}
  166. end,
  167. RoleACL
  168. end,
  169. Record#uce_user.roles),
  170. {ok, lists:flatten(ACL)}.
  171. %
  172. % gen server callbacks
  173. %
  174. -record(state, {
  175. domain,
  176. user,
  177. presences = []
  178. }).
  179. init([Domain, #uce_user{id=Uid} = User]) ->
  180. gproc:add_local_name({Domain, uid, Uid}),
  181. timer:send_after(config:get(timeout_refresh) * 1000, check_timeout),
  182. {ok, #state{domain=Domain, user=User}}.
  183. handle_call(get_user, _From, #state{user=User} = State) ->
  184. {reply, {ok, User}, State};
  185. handle_call({add_presence, #uce_presence{id=Sid}=Presence}, _From, #state{domain=Domain, presences=Presences} = State) ->
  186. gproc:add_local_name({Domain, sid, Sid}),
  187. {reply, ok, State#state{presences=[Presence|Presences]}};
  188. handle_call({get_presence, Sid}, _From, #state{presences=Presences} = State) ->
  189. {reply, get_presence_by_sid(Sid, Presences), State};
  190. handle_call({update_presence, #uce_presence{id=Sid}=NewPresence}, _From, #state{presences=Presences} = State) ->
  191. {Presence, NewPresences} = delete_presence_from_sid(Sid, Presences),
  192. {reply, {ok, Presence}, State#state{presences=[NewPresence|NewPresences]}};
  193. %%
  194. %% supervisor:terminate_child doesn't work with simple_one_for_one in erlang < R14BO3
  195. %%
  196. handle_call(stop, _From, State) ->
  197. {stop, "normal", ok, State}.
  198. handle_cast({update_user, User}, State) ->
  199. {noreply, State#state{user=User}};
  200. handle_cast({add_stream, Sid}, #state{presences=Presences} = State) ->
  201. {Presence, NewPresences} = delete_presence_from_sid(Sid, Presences),
  202. NbStream = Presence#uce_presence.streams + 1,
  203. {noreply, State#state{presences=[Presence#uce_presence{streams=NbStream}|NewPresences]}};
  204. handle_cast({remove_stream, Sid}, #state{presences=Presences} = State) ->
  205. {Presence, NewPresences} = delete_presence_from_sid(Sid, Presences),
  206. NbStream = Presence#uce_presence.streams - 1,
  207. {noreply, State#state{presences=[Presence#uce_presence{streams=NbStream,
  208. last_activity=utils:now()}|NewPresences]}};
  209. handle_cast({delete_presence, Sid}, #state{domain=Domain, user=User, presences=Presences} = State) ->
  210. {PresenceToDelete, NewPresences} = delete_presence_from_sid(Sid, Presences),
  211. ok = disconnect_from_meetings(Domain, User, PresenceToDelete, NewPresences),
  212. case NewPresences of
  213. [] ->
  214. {stop, normal, State#state{presences=NewPresences}};
  215. _Other ->
  216. {noreply, State#state{presences=NewPresences}}
  217. end.
  218. handle_info(check_timeout, #state{domain=Domain,
  219. presences=Presences} = State) ->
  220. cleanup_presence(Domain, Presences, utils:now()),
  221. timer:send_after(config:get(timeout_refresh) * 1000, check_timeout),
  222. {noreply, State}.
  223. terminate(_Reason, _State) ->
  224. ok.
  225. code_change(_OldVsn, State, _Extra) ->
  226. {ok, State}.
  227. %
  228. % Private function
  229. %
  230. start_link(Domain, User) ->
  231. gen_server:start_link(?MODULE, [Domain, User], []).
  232. -spec exists_by_name(domain(), list()) -> true | false | erlang:throw({error, any()}).
  233. exists_by_name(Domain, Name) ->
  234. case catch get_by_name(Domain, Name) of
  235. {error, not_found} ->
  236. false;
  237. {error, Reason} ->
  238. throw({error, Reason});
  239. {ok, _User}->
  240. true
  241. end.
  242. -spec get_presence_by_sid(sid(), list(presence())) -> {ok, presence()} | {error, not_found}.
  243. get_presence_by_sid(_Sid, []) ->
  244. {error, not_found};
  245. get_presence_by_sid(Sid, [#uce_presence{id=Sid} = Presence|_Presences]) ->
  246. {ok, Presence};
  247. get_presence_by_sid(Sid, [_Presence|Presences]) ->
  248. get_presence_by_sid(Sid, Presences).
  249. get_pid_of(Domain, Uid) ->
  250. case gproc:lookup_local_name({Domain, uid, Uid}) of
  251. undefined ->
  252. {error, not_found};
  253. Pid ->
  254. {ok, Pid}
  255. end.
  256. %
  257. % Cleanup old presence
  258. %
  259. cleanup_presence(Domain, [#uce_presence{id=Sid,
  260. streams=0,
  261. last_activity=LastActivity,
  262. timeout=Timeout}|Rest], Now) ->
  263. if
  264. LastActivity + (Timeout * 1000) < Now ->
  265. {ok, deleted} = uce_presence:delete(Domain, Sid),
  266. ?COUNTER(timeout);
  267. true ->
  268. nothing
  269. end,
  270. cleanup_presence(Domain, Rest, Now);
  271. cleanup_presence(Domain, [_Presence|Rest], Now) ->
  272. cleanup_presence(Domain, Rest, Now);
  273. cleanup_presence(_Domain, [], _Now) ->
  274. ok.
  275. %
  276. % Return all presence associated to the user
  277. %
  278. -spec get_all_meetings_of_user(list(presence()), uid()) -> list(meeting_id()) | [].
  279. get_all_meetings_of_user(Presences) ->
  280. get_all_meetings_of_user(Presences, []).
  281. get_all_meetings_of_user([#uce_presence{meetings=Meetings}|Rest], Result) ->
  282. get_all_meetings_of_user(Rest, Meetings ++ Result);
  283. % the end
  284. get_all_meetings_of_user([], Result) ->
  285. Result.
  286. %
  287. % Cleanup presence in meetings
  288. %
  289. disconnect_from_meetings(Domain, #uce_user{id=Uid}, #uce_presence{meetings=Meetings}, Presences) ->
  290. UserMeetings = sets:to_list(sets:subtract(sets:from_list(Meetings), sets:from_list(get_all_meetings_of_user(Presences)))),
  291. clean_meetings(Domain, Uid, UserMeetings).
  292. clean_meetings(Domain, Uid, Meetings) ->
  293. clean_meeting(Domain, Meetings, Uid),
  294. uce_event:add(Domain, #uce_event{id=none,
  295. from=Uid,
  296. type="internal.presence.delete",
  297. location=""}),
  298. ok.
  299. clean_meeting(_Domain, [], _Uid) ->
  300. ok;
  301. clean_meeting(Domain, [Meeting|Meetings], Uid) ->
  302. try uce_event:add(Domain, #uce_event{id=none,
  303. from=Uid,
  304. type="internal.roster.delete",
  305. location=Meeting}) of
  306. {ok, _Id} ->
  307. try uce_meeting:leave(Domain, Meeting, Uid) of
  308. {ok, updated} ->
  309. ok
  310. catch
  311. {error, Reason} ->
  312. ?ERROR_MSG("Error when cleanup meeting presence of ~p : ~p", [Uid, Reason])
  313. end
  314. catch
  315. {error, Reason} ->
  316. ?ERROR_MSG("Error when cleanup roster presence of ~p : ~p", [Uid, Reason])
  317. end,
  318. clean_meeting(Domain, Meetings, Uid).
  319. delete_presence_from_sid(Sid, Presences) ->
  320. {ok, Presence} = get_presence_by_sid(Sid, Presences),
  321. NewPresences = lists:delete(Presence, Presences),
  322. {Presence, NewPresences}.