PageRenderTime 43ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/common/ASC.SignalR.Base/Hubs/Chat/Chat.cs

https://github.com/dc0d/ONLYOFFICE-Server
C# | 508 lines | 433 code | 33 blank | 42 comment | 50 complexity | 56401797ab0e1bbe915ed5c4afa6a36b MD5 | raw file
Possible License(s): GPL-2.0, MPL-2.0-no-copyleft-exception
  1. /*
  2. (c) Copyright Ascensio System SIA 2010-2014
  3. This program is a free software product.
  4. You can redistribute it and/or modify it under the terms
  5. of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
  6. Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
  7. to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
  8. any third-party rights.
  9. This program is distributed WITHOUT ANY WARRANTY; without even the implied warranty
  10. of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
  11. the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
  12. You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
  13. The interactive user interfaces in modified source and object code versions of the Program must
  14. display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
  15. Pursuant to Section 7(b) of the License you must retain the original Product logo when
  16. distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
  17. trademark law for use of our trademarks.
  18. All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
  19. content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
  20. International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
  21. */
  22. using ASC.Common.Security.Authentication;
  23. using ASC.Core;
  24. using ASC.Core.Common.Notify.Jabber;
  25. using ASC.Core.Tenants;
  26. using ASC.Core.Users;
  27. using ASC.Data.Storage;
  28. using ASC.Web.Core.Jabber;
  29. using log4net;
  30. using Microsoft.AspNet.SignalR;
  31. using Microsoft.AspNet.SignalR.Hubs;
  32. using System;
  33. using System.Collections.Generic;
  34. using System.Globalization;
  35. using System.Linq;
  36. using System.Runtime.CompilerServices;
  37. using System.Security;
  38. using System.Security.Principal;
  39. using System.Threading.Tasks;
  40. using System.Web;
  41. namespace ASC.SignalR.Base.Hubs.Chat
  42. {
  43. [AuthorizeHub]
  44. [HubName("c")]
  45. public class Chat : Hub
  46. {
  47. public readonly static ConnectionMapping Connections = new ConnectionMapping();
  48. public readonly static UserStateMapping States = new UserStateMapping();
  49. public readonly static JabberServiceClient JabberServiceClient = new JabberServiceClient();
  50. private readonly static ILog ChatLog = LogManager.GetLogger(typeof(Chat));
  51. private readonly static object FirstChatLoadingSyncRoot = new object();
  52. private volatile static int allConnectionsCount;
  53. private volatile static bool firstChatLoading = true;
  54. private const string websockets = "webSockets";
  55. private const string transport = "transport";
  56. private const string stateNumber = "State";
  57. private const string soundPath = "usercontrols/common/smallchat/css/sounds/chat";
  58. private const byte userOnline = 1;
  59. public const byte UserOffline = 4;
  60. public const byte TraceError = 0;
  61. public const byte TraceDebug = 1;
  62. public override Task OnDisconnected()
  63. {
  64. var user = Context.Request.Environment["server.User"] as GenericPrincipal;
  65. if (user == null)
  66. {
  67. AuthorizeHubAttribute.Authorize(Context.Request);
  68. }
  69. try
  70. {
  71. DisconnectUser();
  72. }
  73. catch (Exception)
  74. {
  75. }
  76. return base.OnDisconnected();
  77. }
  78. // Method for JS-clients
  79. [HubMethodName("s")]
  80. public void Send(string calleeUserName, string messageText)
  81. {
  82. var user = (IUserAccount)Context.User.Identity;
  83. CoreContext.TenantManager.SetCurrentTenant(user.Tenant);
  84. var currentUser = CoreContext.UserManager.GetUsers(user.ID);
  85. if (calleeUserName != string.Empty && CoreContext.UserManager.GetUserByUserName(calleeUserName).Equals(Core.Users.Constants.LostUser))
  86. {
  87. TraceMessage(TraceError, String.Format("Can't get UserInfo by calleeUserName={0}, TenantId={1}.", calleeUserName, currentUser.Tenant));
  88. throw new HubException();
  89. }
  90. TraceMessage(TraceDebug, String.Format("Send: calleeUserName={0}, messageText={1}", calleeUserName, messageText));
  91. var callerUserName = currentUser.UserName.ToLowerInvariant();
  92. var message = new MessageClass
  93. {
  94. UserName = callerUserName,
  95. Text = messageText
  96. };
  97. if (calleeUserName != string.Empty)
  98. {
  99. // send
  100. Clients.Group(currentUser.Tenant + calleeUserName).s(message, calleeUserName);
  101. // send
  102. Clients.OthersInGroup(currentUser.Tenant + callerUserName).s(message, calleeUserName);
  103. }
  104. try
  105. {
  106. JabberServiceClient.SendMessage(currentUser.Tenant, callerUserName, calleeUserName, messageText);
  107. }
  108. catch (Exception e)
  109. {
  110. TraceMessage(TraceError, String.Format("Error on sending message to Jabber service. CallerUserName={0}, TenantId={1}. {2}",
  111. callerUserName, currentUser.Tenant, e));
  112. // error
  113. Clients.Caller.e();
  114. }
  115. }
  116. [HubMethodName("gs")]
  117. public void GetStates()
  118. {
  119. var user = (IUserAccount)Context.User.Identity;
  120. CoreContext.TenantManager.SetCurrentTenant(user.Tenant);
  121. var currentUserInfo = CoreContext.UserManager.GetUsers(user.ID);
  122. var currentUserName = currentUserInfo.UserName.ToLowerInvariant();
  123. TraceMessage(TraceDebug, String.Format("Get States currentUserName={0}", currentUserName));
  124. lock (States.SyncRoot)
  125. {
  126. GetStatesFromJabber();
  127. // statesRetrieved
  128. Clients.Caller.sr(States.GetStatesOfTenant(currentUserInfo.Tenant).
  129. Where(s => s.Key != currentUserName && s.Value != UserOffline).ToDictionary(s => s.Key, s => s.Value));
  130. }
  131. }
  132. [HubMethodName("gci")]
  133. public Tuple<string, byte> GetContactInfo(string userName)
  134. {
  135. var u = (IUserAccount)Context.User.Identity;
  136. CoreContext.TenantManager.SetCurrentTenant(u.Tenant);
  137. var user = CoreContext.UserManager.GetUserByUserName(userName);
  138. TraceMessage(TraceDebug, String.Format("Get Contact Info userName={0}", userName));
  139. if (user.Equals(Core.Users.Constants.LostUser))
  140. {
  141. TraceMessage(TraceError, String.Format("Can't getUserInfo by userName={0}, TenantId={1}.",
  142. userName, CoreContext.TenantManager.GetCurrentTenant().TenantId));
  143. throw new HubException();
  144. }
  145. return Tuple.Create(user.DisplayUserName(), States.GetState(user.Tenant, userName));
  146. }
  147. [HubMethodName("gid")]
  148. public void GetInitData()
  149. {
  150. var user = (IUserAccount)Context.User.Identity;
  151. CoreContext.TenantManager.SetCurrentTenant(user.Tenant);
  152. var currentUserInfo = CoreContext.UserManager.GetUsers(user.ID);
  153. TraceMessage(TraceDebug, String.Format("Get Init Data userName={0}", currentUserInfo.UserName));
  154. lock (States.SyncRoot)
  155. {
  156. GetStatesFromJabber();
  157. var states = States.GetStatesOfTenant(currentUserInfo.Tenant);
  158. var users = GetUsers(states);
  159. // initDataRetrieved
  160. Clients.Caller.idr(currentUserInfo.UserName.ToLowerInvariant(),
  161. currentUserInfo.DisplayUserName(), users, currentUserInfo.Tenant,
  162. CoreContext.TenantManager.GetCurrentTenant().GetTenantDomain(false),
  163. WebPath.GetPath(soundPath));
  164. }
  165. }
  166. [HubMethodName("st")]
  167. public void SendTyping(string calleeUserName)
  168. {
  169. var user = (IUserAccount)Context.User.Identity;
  170. CoreContext.TenantManager.SetCurrentTenant(user.Tenant);
  171. var currentUser = CoreContext.UserManager.GetUsers(user.ID);
  172. if (CoreContext.UserManager.GetUserByUserName(calleeUserName).Equals(Core.Users.Constants.LostUser))
  173. {
  174. TraceMessage(TraceError, String.Format("Can't getUserInfo by calleeUserName = {0}, TenantId = {1}.",
  175. calleeUserName, currentUser.Tenant));
  176. throw new HubException();
  177. }
  178. // sendTypingSignal
  179. Clients.Group(currentUser.Tenant + calleeUserName).sts(currentUser.UserName.ToLowerInvariant());
  180. }
  181. [HubMethodName("sstt")]
  182. public async Task SendStateToTenant(byte state)
  183. {
  184. var user = (IUserAccount)Context.User.Identity;
  185. CoreContext.TenantManager.SetCurrentTenant(user.Tenant);
  186. var currentUser = CoreContext.UserManager.GetUsers(user.ID);
  187. var userName = currentUser.UserName.ToLowerInvariant();
  188. TraceMessage(TraceDebug, String.Format("Send State To Tenant userName={0}, state={1}", userName, state));
  189. States.Add(currentUser.Tenant, userName, state);
  190. // setState
  191. Clients.OthersInGroup(currentUser.Tenant.ToString(CultureInfo.InvariantCulture)).ss(userName, state, false);
  192. try
  193. {
  194. await JabberServiceClient.SendState(currentUser.Tenant, userName, state);
  195. }
  196. catch (Exception e)
  197. {
  198. TraceMessage(TraceError, String.Format("Error on sending state to Jabber service. UserName = {0}, TenantId = {1}. {2}",
  199. userName, currentUser.Tenant, e));
  200. // error
  201. Clients.Caller.e();
  202. }
  203. }
  204. [HubMethodName("grm")]
  205. public MessageClass[] GetRecentMessages(string calleeUserName, int id)
  206. {
  207. var user = (IUserAccount)Context.User.Identity;
  208. CoreContext.TenantManager.SetCurrentTenant(user.Tenant);
  209. var currentUser = CoreContext.UserManager.GetUsers(user.ID);
  210. var calleeUser = CoreContext.UserManager.GetUserByUserName(calleeUserName);
  211. if (calleeUserName != string.Empty && calleeUser.Equals(Core.Users.Constants.LostUser))
  212. {
  213. TraceMessage(TraceError, String.Format("Can't getUserInfo by calleeUserName = {0}, TenantId = {1}.", calleeUserName, currentUser.Tenant));
  214. throw new HubException();
  215. }
  216. MessageClass[] recentMessages = null;
  217. var callerUserName = currentUser.UserName.ToLowerInvariant();
  218. TraceMessage(TraceDebug, String.Format("Get Recent Messages calleeUserName={0}, callerUserName={1}, id={2}", calleeUserName, callerUserName, id));
  219. try
  220. {
  221. recentMessages = JabberServiceClient.GetRecentMessages(currentUser.Tenant,
  222. callerUserName, calleeUserName == string.Empty ? null : calleeUserName, id);
  223. if (recentMessages != null)
  224. {
  225. for (var i = 0; i < recentMessages.Length; i++)
  226. {
  227. recentMessages[i].DateTime = TenantUtil.DateTimeFromUtc(recentMessages[i].DateTime.AddMilliseconds(1));
  228. if (recentMessages[i].UserName == null ||
  229. String.Equals(recentMessages[i].UserName, calleeUserName, StringComparison.InvariantCultureIgnoreCase))
  230. {
  231. recentMessages[i].UserName = calleeUserName;
  232. }
  233. else
  234. {
  235. recentMessages[i].UserName = callerUserName;
  236. }
  237. }
  238. }
  239. }
  240. catch (Exception e)
  241. {
  242. TraceMessage(TraceError, String.Format("Error on receiving recent messages from Jabber service. UserName = {0}, TenantId = {1}. {2}, {3}, {4}",
  243. currentUser.UserName, currentUser.Tenant, e, e.StackTrace, e.InnerException != null ? e.InnerException.Message : string.Empty));
  244. // error
  245. Clients.Caller.e();
  246. }
  247. return recentMessages;
  248. }
  249. [HubMethodName("p")]
  250. public void Ping()
  251. {
  252. var user = (IUserAccount)Context.User.Identity;
  253. CoreContext.TenantManager.SetCurrentTenant(user.Tenant);
  254. var userInfo = CoreContext.UserManager.GetUsers(user.ID);
  255. TraceMessage(TraceDebug, String.Format("Ping from JS client: {0}", userInfo.ID));
  256. try
  257. {
  258. JabberServiceClient.Ping(userInfo.ID.ToString(), userInfo.Tenant, userInfo.UserName, States.GetState(userInfo.Tenant, userInfo.UserName));
  259. }
  260. catch (Exception e)
  261. {
  262. TraceMessage(TraceError, String.Format("Error on Ping to Jabber. {0} {1} {2}",
  263. e, e.StackTrace, e.InnerException != null ? e.InnerException.Message : string.Empty));
  264. }
  265. }
  266. [HubMethodName("cu")]
  267. public void ConnectUser()
  268. {
  269. var user = Context.Request.Environment["server.User"] as GenericPrincipal;
  270. var tr = Context.QueryString[transport];
  271. if (user != null)
  272. {
  273. var userAccount = user.Identity as IUserAccount;
  274. if (userAccount == null)
  275. {
  276. TraceMessage(TraceError, "Unknown user tries to connect to SignalR hub.");
  277. throw new SecurityException();
  278. }
  279. if (tr == websockets)
  280. {
  281. CoreContext.TenantManager.SetCurrentTenant(userAccount.Tenant);
  282. }
  283. byte state;
  284. try
  285. {
  286. state = Convert.ToByte(Context.QueryString[stateNumber]);
  287. }
  288. catch (Exception e)
  289. {
  290. TraceMessage(TraceError, String.Format("Possible wrong state on connecting, state = {0}. {1}",
  291. Context.QueryString[stateNumber], e));
  292. state = userOnline;
  293. }
  294. var currentUser = CoreContext.UserManager.GetUsers(userAccount.ID);
  295. if (!currentUser.Equals(Core.Users.Constants.LostUser))
  296. {
  297. var currentUserName = currentUser.UserName.ToLowerInvariant();
  298. Groups.Add(Context.ConnectionId, currentUser.Tenant + currentUserName);
  299. Groups.Add(Context.ConnectionId, currentUser.Tenant.ToString(CultureInfo.InvariantCulture));
  300. var connectionsCount = Connections.Add(currentUser.Tenant, currentUserName, Context.ConnectionId);
  301. TraceMessage(TraceDebug,
  302. String.Format("Add Connection. {0}. Count: {1}", currentUserName, ++allConnectionsCount));
  303. States.Add(currentUser.Tenant, currentUserName, state);
  304. // setState
  305. Clients.OthersInGroup(currentUser.Tenant.ToString(CultureInfo.InvariantCulture))
  306. .ss(currentUserName, state, false);
  307. if (connectionsCount == 1)
  308. {
  309. try
  310. {
  311. JabberServiceClient.AddXmppConnection(currentUser.ID.ToString(), currentUserName,
  312. state, currentUser.Tenant);
  313. }
  314. catch (Exception e)
  315. {
  316. TraceMessage(TraceError,
  317. String.Format(
  318. "Error on adding of Jabber connection. Username={0}. {1} {2} {3}",
  319. currentUserName, e, e.StackTrace,
  320. e.InnerException != null ? e.InnerException.Message : string.Empty));
  321. }
  322. }
  323. else
  324. {
  325. // setStatus
  326. Clients.OthersInGroup(currentUser.Tenant + currentUser.UserName).sst(state);
  327. }
  328. }
  329. else
  330. {
  331. TraceMessage(TraceError, "Unknown user tries to connect.");
  332. throw new SecurityException();
  333. }
  334. }
  335. }
  336. [HubMethodName("dcu")]
  337. public void DisconnectUser()
  338. {
  339. var user = Context.Request.Environment["server.User"] as GenericPrincipal;
  340. if (user != null)
  341. {
  342. var userAccount = user.Identity as IUserAccount;
  343. if (userAccount != null)
  344. {
  345. CoreContext.TenantManager.SetCurrentTenant(userAccount.Tenant);
  346. }
  347. else
  348. {
  349. TraceMessage(TraceError, String.Format("Unknown request without user.Identity as IUserAccount, url={0}",
  350. Context.Request.Url));
  351. throw new SecurityException();
  352. }
  353. var currentUser = CoreContext.UserManager.GetUsers(userAccount.ID);
  354. if (!currentUser.Equals(Core.Users.Constants.LostUser))
  355. {
  356. var currentUserName = currentUser.UserName.ToLowerInvariant();
  357. Groups.Remove(Context.ConnectionId, currentUser.Tenant + currentUserName);
  358. Groups.Remove(Context.ConnectionId, currentUser.Tenant.ToString(CultureInfo.InvariantCulture));
  359. // if only one connection
  360. if (Connections.GetConnectionsCount(currentUser.Tenant, currentUserName) == 1)
  361. {
  362. var connectionsCount = Connections.Remove(currentUser.Tenant, currentUserName, Context.ConnectionId);
  363. TraceMessage(TraceDebug, String.Format("Remove Connection. {0}. Count: {1}", currentUserName, --allConnectionsCount));
  364. if (connectionsCount == 0)
  365. {
  366. try
  367. {
  368. if (JabberServiceClient.RemoveXmppConnection(currentUser.ID.ToString(), currentUserName, currentUser.Tenant))
  369. {
  370. States.Add(currentUser.Tenant, currentUserName, UserOffline);
  371. // setState
  372. Clients.OthersInGroup(currentUser.Tenant.ToString(CultureInfo.InvariantCulture)).ss(currentUserName, UserOffline, false);
  373. }
  374. }
  375. catch (Exception e)
  376. {
  377. States.Add(currentUser.Tenant, currentUserName, UserOffline);
  378. // setState
  379. Clients.OthersInGroup(currentUser.Tenant.ToString(CultureInfo.InvariantCulture)).ss(currentUserName, UserOffline, false);
  380. TraceMessage(TraceError, String.Format("Error on removing of Jabber connection. Username={0}. {1} {2} {3}",
  381. currentUserName, e, e.StackTrace, e.InnerException != null ? e.InnerException.Message : string.Empty));
  382. }
  383. }
  384. }
  385. else
  386. {
  387. Connections.Remove(currentUser.Tenant, currentUserName, Context.ConnectionId);
  388. TraceMessage(TraceDebug, String.Format("Remove Connection. {0}. Count: {1}", currentUserName, --allConnectionsCount));
  389. }
  390. }
  391. else
  392. {
  393. TraceMessage(TraceError, "Unknown user tries to disconnect.");
  394. throw new SecurityException();
  395. }
  396. }
  397. else
  398. {
  399. TraceMessage(TraceError, String.Format("Unknown user tries to disconnect from SignalR hub."));
  400. throw new SecurityException();
  401. }
  402. }
  403. private static void GetStatesFromJabber()
  404. {
  405. if (!firstChatLoading) return;
  406. lock (FirstChatLoadingSyncRoot)
  407. {
  408. if (firstChatLoading)
  409. {
  410. try
  411. {
  412. var jabberStates = JabberServiceClient.GetAllStates();
  413. foreach (var tenantStatesPair in jabberStates)
  414. {
  415. foreach (var statesPair in tenantStatesPair.Value)
  416. {
  417. States.Add(CoreContext.TenantManager.GetTenant(tenantStatesPair.Key).TenantId, statesPair.Key, statesPair.Value);
  418. }
  419. }
  420. firstChatLoading = false;
  421. }
  422. catch (Exception e)
  423. {
  424. TraceMessage(TraceError, String.Format("Can't get all states from Jabber. {0} {1} {2}",
  425. e, e.StackTrace, e.InnerException != null ? e.InnerException.Message : string.Empty));
  426. }
  427. }
  428. }
  429. }
  430. private static UserStr[] GetUsers(IReadOnlyDictionary<string, byte> states)
  431. {
  432. var users = CoreContext.UserManager.GetUsers().Where(user => !user.IsMe()).SortByUserName();
  433. var usersArray = new UserStr[users.Count];
  434. for (var i = 0; i < users.Count; i++)
  435. {
  436. byte state;
  437. var userName = users[i].UserName.ToLowerInvariant();
  438. if (!states.TryGetValue(userName, out state))
  439. {
  440. state = UserOffline;
  441. }
  442. usersArray[i] = new UserStr { UserName = userName, DisplayUserName = users[i].DisplayUserName(), State = state };
  443. }
  444. return usersArray;
  445. }
  446. public static void TraceMessage(byte messageState, string message, [CallerMemberName] string memberName = "",
  447. [CallerFilePath] string filePath = "", [CallerLineNumber] int lineNumber = 0)
  448. {
  449. string name;
  450. try
  451. {
  452. name = HttpContext.Current.User.Identity.Name;
  453. }
  454. catch
  455. {
  456. name = string.Empty;
  457. }
  458. switch (messageState)
  459. {
  460. case TraceError:
  461. ChatLog.ErrorFormat(message + " {0}:{1}:{2}. {3}", filePath, memberName, lineNumber, name);
  462. break;
  463. case TraceDebug:
  464. ChatLog.DebugFormat(message + " {0}:{1}:{2}. {3}", filePath, memberName, lineNumber, name);
  465. break;
  466. }
  467. }
  468. }
  469. }