From a4c097dd0d715a7f3f5c699acce3644698ca9edf Mon Sep 17 00:00:00 2001 From: Paul Schneider Date: Fri, 14 Jun 2019 10:03:51 +0100 Subject: [PATCH] implemented some not tested kick --- .gitignore | 2 +- omnisharp.json | 2 +- omnisharp.log | 1 + src/Yavsc.Abstract/Chat/ChatHubConstants.cs | 24 ++ .../Messaging/NotificationTypes.cs | 4 + src/Yavsc.Server/Constants.cs | 9 - src/Yavsc.Server/Models/Chat/ChatRoom.cs | 2 +- .../ViewModels/Chat/ChatUserInfo.cs | 6 + .../Relationship/ChatApiController.cs | 16 +- src/Yavsc/Hubs/ChatHub.cs | 249 +++++++------ src/Yavsc/Hubs/ChatRoomInfo.cs | 13 +- src/Yavsc/Resources/Yavsc.ChatHub.en.resx | 66 ++++ src/Yavsc/Resources/Yavsc.ChatHub.fr.resx | 66 ++++ ....Services.ChatHubConnectionManager.fr.resx | 67 ++++ src/Yavsc/Services/ChatHubConnexionManager.cs | 333 ++++++++++++++++++ src/Yavsc/Services/IConnexionManager.cs | 34 ++ src/Yavsc/Services/YavscMessageSender.cs | 9 +- src/Yavsc/Startup/Startup.cs | 6 +- .../{GCMDevices => Devices}/Delete.cshtml | 0 .../{GCMDevices => Devices}/Details.cshtml | 0 .../{GCMDevices => Devices}/Index.cshtml | 8 +- src/Yavsc/Views/Shared/_Layout.cshtml | 2 +- src/Yavsc/omnisharp.json | 19 - src/Yavsc/wwwroot/js/chat.js | 2 +- src/omnisharp.log | 1 + .../Remoting.cs} | 0 26 files changed, 756 insertions(+), 185 deletions(-) create mode 100644 omnisharp.log create mode 100644 src/Yavsc.Abstract/Chat/ChatHubConstants.cs create mode 100644 src/Yavsc/Resources/Yavsc.ChatHub.en.resx create mode 100644 src/Yavsc/Resources/Yavsc.ChatHub.fr.resx create mode 100644 src/Yavsc/Resources/Yavsc.Services.ChatHubConnectionManager.fr.resx create mode 100644 src/Yavsc/Services/ChatHubConnexionManager.cs create mode 100644 src/Yavsc/Services/IConnexionManager.cs rename src/Yavsc/Views/{GCMDevices => Devices}/Delete.cshtml (100%) rename src/Yavsc/Views/{GCMDevices => Devices}/Details.cshtml (100%) rename src/Yavsc/Views/{GCMDevices => Devices}/Index.cshtml (80%) delete mode 100644 src/Yavsc/omnisharp.json create mode 100644 src/omnisharp.log rename src/test/{WIP/YavscWorkInProgress.cs => Mandatory/Remoting.cs} (100%) diff --git a/.gitignore b/.gitignore index 2fb13997..73c5bf07 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ private/ DataProtection-Keys/ RSA-Params.json appsettings.*.json +omnisharp.json /packages/ /src/Yavsc/Avatars-*/ @@ -30,4 +31,3 @@ appsettings.*.json /src/Yavsc/bower_components/ /src/Yavsc/AppData*/ /src/test/testingrepo/ - diff --git a/omnisharp.json b/omnisharp.json index 8c4085f3..09d05355 100644 --- a/omnisharp.json +++ b/omnisharp.json @@ -2,7 +2,7 @@ "Dnx": { "enabled": true, "enablePackageRestore": false, - "projects": "src/**/project.json;test/**/project.json" + "projects": "src/*/project.json" }, "MSBuild": { "enabled": false diff --git a/omnisharp.log b/omnisharp.log new file mode 100644 index 00000000..c68330fd --- /dev/null +++ b/omnisharp.log @@ -0,0 +1 @@ +["-s","/home/paul/workspace/yavsc/src","--hostPID","7920","DotNet:enablePackageRestore=false","--encoding","utf-8","--loglevel","warning","FileOptions:SystemExcludeSearchPatterns:0=**/.git","FileOptions:SystemExcludeSearchPatterns:1=**/.svn","FileOptions:SystemExcludeSearchPatterns:2=**/.hg","FileOptions:SystemExcludeSearchPatterns:3=**/CVS","FileOptions:SystemExcludeSearchPatterns:4=**/.DS_Store"] \ No newline at end of file diff --git a/src/Yavsc.Abstract/Chat/ChatHubConstants.cs b/src/Yavsc.Abstract/Chat/ChatHubConstants.cs new file mode 100644 index 00000000..e68c719d --- /dev/null +++ b/src/Yavsc.Abstract/Chat/ChatHubConstants.cs @@ -0,0 +1,24 @@ +namespace Yavsc.Abstract.Chat +{ + public static class ChatHubConstants + { + public const string HubGroupAuthenticated = "authenticated"; + public const string HubGroupAnonymous = "anonymous"; + public const string HubGroupCops= "cops"; + public const string HubGroupRomsPrefix = "room_"; + public const int MaxChanelName = 255; + + public const string HubGroupFollowingPrefix = "fol "; + public const string AnonymousUserNamePrefix = "?"; + public const string KeyParamChatUserName = "username"; + + public const string JustCreatedBy = "just created by "; + public const string LabYouNotOp = "you're no op."; + public const string LabNoSuchUser = "No such user"; + public const string LabNoSuchChan = "No such chan"; + public const string HopWontKickOp = "Half operator cannot kick any operator"; + public const string LabAuthChatUser = "Authenticated chat user"; + public const string NoKickOnCop = "No, you won´t, you´ĺl never do kick a cop, it is the bad."; + public const string LabnoJoinNoSend = "LabnoJoinNoSend"; + } +} \ No newline at end of file diff --git a/src/Yavsc.Abstract/Messaging/NotificationTypes.cs b/src/Yavsc.Abstract/Messaging/NotificationTypes.cs index fe281852..622e3f2d 100644 --- a/src/Yavsc.Abstract/Messaging/NotificationTypes.cs +++ b/src/Yavsc.Abstract/Messaging/NotificationTypes.cs @@ -6,6 +6,10 @@ namespace Yavsc public const string Reconnected = "reconnected"; public const string UserPart = "userpart"; public const string UserJoin = "userjoin"; + public const string Kick = "kick"; + public const string Ban = "ban"; + public const string KickBan = "kickban"; + public const string Gline = "gline"; public const string PrivateMessageDenied = "denied_pv"; public const string Error = "error"; public const string ContactRefused = "contact_refused"; diff --git a/src/Yavsc.Server/Constants.cs b/src/Yavsc.Server/Constants.cs index b1327e25..72547829 100644 --- a/src/Yavsc.Server/Constants.cs +++ b/src/Yavsc.Server/Constants.cs @@ -57,15 +57,6 @@ namespace Yavsc { "openid", "profile", "email", "https://www.googleapis.com/auth/calendar" }; public static readonly string NoneCode = "none"; - public const string HubGroupAuthenticated = "authenticated"; - public const string HubGroupAnonymous = "anonymous"; - public const string HubGroupCops= "cops"; - public const string HubGroupRomsPrefix = "room_"; - public const int MaxChanelName = 255; - public const string HubGroupFollowingPrefix = "fol "; - public const string AnonymousUserNamePrefix = "?"; - public const string KeyParamChatUserName = "username"; - public const string LabAuthChatUser = "Authenticated chat user"; } } diff --git a/src/Yavsc.Server/Models/Chat/ChatRoom.cs b/src/Yavsc.Server/Models/Chat/ChatRoom.cs index 909d58fb..f0147f82 100644 --- a/src/Yavsc.Server/Models/Chat/ChatRoom.cs +++ b/src/Yavsc.Server/Models/Chat/ChatRoom.cs @@ -13,7 +13,7 @@ namespace Yavsc.Models.Chat public string Topic { get; set; } [Key] - [StringLengthAttribute(Constants.MaxChanelName, MinimumLength=3)] + [StringLengthAttribute(ChatHubConstants.MaxChanelName, MinimumLength=3)] public string Name { get; set;} public string OwnerId { get; set; } diff --git a/src/Yavsc.Server/ViewModels/Chat/ChatUserInfo.cs b/src/Yavsc.Server/ViewModels/Chat/ChatUserInfo.cs index 561b8105..66350be9 100644 --- a/src/Yavsc.Server/ViewModels/Chat/ChatUserInfo.cs +++ b/src/Yavsc.Server/ViewModels/Chat/ChatUserInfo.cs @@ -2,6 +2,12 @@ using System.Collections.Generic; using Yavsc.Models.Chat; namespace Yavsc.ViewModels.Chat {  + + public class ChannelShortInfo { + public string RoomName {get; set;} + public string Topic { get; set; } + } + public class ChatUserInfo : IChatUserInfo { diff --git a/src/Yavsc/ApiControllers/Relationship/ChatApiController.cs b/src/Yavsc/ApiControllers/Relationship/ChatApiController.cs index fe10c2b8..e63789ed 100644 --- a/src/Yavsc/ApiControllers/Relationship/ChatApiController.cs +++ b/src/Yavsc/ApiControllers/Relationship/ChatApiController.cs @@ -7,7 +7,7 @@ using Microsoft.AspNet.Mvc; using Microsoft.AspNet.Identity; using Yavsc.Models; using Yavsc.ViewModels.Chat; - +using Yavsc.Services; namespace Yavsc.Controllers { @@ -17,11 +17,14 @@ namespace Yavsc.Controllers { ApplicationDbContext dbContext; UserManager userManager; + private IConnexionManager _cxManager; public ChatApiController(ApplicationDbContext dbContext, - UserManager userManager) + UserManager userManager, + IConnexionManager cxManager) { this.dbContext = dbContext; this.userManager = userManager; + _cxManager = cxManager; } [HttpGet("users")] @@ -86,10 +89,6 @@ namespace Yavsc.Controllers }); } - public class ChannelShortInfo { - public string RoomName {get; set;} - public string Topic { get; set; } - } /// /// Get firsts 10 biggest channels having @@ -100,7 +99,7 @@ namespace Yavsc.Controllers [HttpGet("chanlist/{chanNamePrefix}")] public IActionResult GetChanList([FromRoute] string chanNamePrefix) { - var list = ChatHub.Channels.Where(c => c.Key.StartsWith(chanNamePrefix)).OrderByDescending(c=>c.Value.Users.Count).Select(c=>new ChannelShortInfo { RoomName= c.Key, Topic = c.Value.Topic }).Take(10); + var list = _cxManager.ListChannels(chanNamePrefix); return Ok(list); } @@ -111,8 +110,7 @@ namespace Yavsc.Controllers [HttpGet("chanlist")] public IActionResult GetChanList() { - return Ok(ChatHub.Channels.OrderByDescending(c=>c.Value.Users.Count).Select(c=> new ChannelShortInfo { RoomName= c.Key, Topic = c.Value.Topic }) - .Take(10)); + return Ok(_cxManager.ListChannels(null)); } } diff --git a/src/Yavsc/Hubs/ChatHub.cs b/src/Yavsc/Hubs/ChatHub.cs index e6b81e2a..acff57ad 100644 --- a/src/Yavsc/Hubs/ChatHub.cs +++ b/src/Yavsc/Hubs/ChatHub.cs @@ -31,16 +31,21 @@ using Microsoft.Extensions.Localization; namespace Yavsc { + using System.Collections.Generic; + using System.ComponentModel.DataAnnotations; using Models; using Models.Chat; + using Yavsc.Abstract.Chat; + using Yavsc.Services; public partial class ChatHub : Hub, IDisposable { + // TODO externalize connexion and room presence management + ApplicationDbContext _dbContext; + private IConnexionManager _cxManager; private IStringLocalizer _localizer; ILogger _logger; - public static ConcurrentDictionary ChatUserNames = new ConcurrentDictionary(); - public static ConcurrentDictionary Channels = new ConcurrentDictionary(); public ChatHub() { @@ -51,24 +56,36 @@ namespace Yavsc var stringLocFactory = scope.ServiceProvider.GetService(); _localizer = stringLocFactory.Create(typeof(ChatHub)); + + _cxManager = scope.ServiceProvider.GetService(); + _cxManager.SetErrorHandler ((context, error) => + { + Clients.Caller.notifyUser(NotificationTypes.Error, context, error); + }); _logger = loggerFactory.CreateLogger(); } + void SetUserName(string cxId, string userName) + { + _cxManager.SetUserName( cxId, userName); + } + public override async Task OnConnected() { bool isAuth = false; + bool isCop = false; string userName = setUserName(); if (Context.User != null) { isAuth = Context.User.Identity.IsAuthenticated; var group = isAuth ? - Constants.HubGroupAuthenticated : Constants.HubGroupAnonymous; + ChatHubConstants.HubGroupAuthenticated : ChatHubConstants.HubGroupAnonymous; // Log ("Cx: " + group); await Groups.Add(Context.ConnectionId, group); if (isAuth) { - _logger.LogInformation(_localizer.GetString(Constants.LabAuthChatUser)); + _logger.LogInformation(_localizer.GetString(ChatHubConstants.LabAuthChatUser)); var userId = _dbContext.Users.First(u => u.UserName == userName).Id; @@ -88,11 +105,16 @@ namespace Yavsc Connected = true }); _dbContext.SaveChanges(); - Clients.Group(Constants.HubGroupFollowingPrefix + userId).notifyuser(NotificationTypes.Connected, userName, null); + Clients.Group(ChatHubConstants.HubGroupFollowingPrefix + userId).notifyuser(NotificationTypes.Connected, userName, null); + isCop = Context.User.IsInRole(Constants.AdminGroupName) ; + if (isCop) + { + await Groups.Add(Context.ConnectionId, ChatHubConstants.HubGroupCops); + } foreach (var uid in _dbContext.CircleMembers.Select(m => m.MemberId)) { - await Groups.Add(Context.ConnectionId, Constants.HubGroupFollowingPrefix + uid); + await Groups.Add(Context.ConnectionId, ChatHubConstants.HubGroupFollowingPrefix + uid); } } else @@ -103,8 +125,9 @@ namespace Yavsc } else { - await Groups.Add(Context.ConnectionId, Constants.HubGroupAnonymous); + await Groups.Add(Context.ConnectionId, ChatHubConstants.HubGroupAnonymous); } + _cxManager.OnConnected(userName, isCop); await base.OnConnected(); } string setUserName() @@ -112,15 +135,15 @@ namespace Yavsc if (Context.User != null) if (Context.User.Identity.IsAuthenticated) { - ChatUserNames[Context.ConnectionId] = Context.User.Identity.Name; + SetUserName(Context.ConnectionId, Context.User.Identity.Name); return Context.User.Identity.Name; } anonymousSequence++; - var queryUname = Context.Request.QueryString[Constants.KeyParamChatUserName]; + var queryUname = Context.Request.QueryString[ChatHubConstants.KeyParamChatUserName]; - var aname = $"{Constants.AnonymousUserNamePrefix}{queryUname}{anonymousSequence}"; - ChatUserNames[Context.ConnectionId] = aname; + var aname = $"{ChatHubConstants.AnonymousUserNamePrefix}{queryUname}{anonymousSequence}"; + SetUserName(Context.ConnectionId, aname); return aname; } @@ -133,7 +156,7 @@ namespace Yavsc { var user = _dbContext.Users.FirstOrDefault(u => u.UserName == userName); var userId = user.Id; - Clients.Group(Constants.HubGroupFollowingPrefix + userId).notifyuser(NotificationTypes.DisConnected, userName, null); + Clients.Group(ChatHubConstants.HubGroupFollowingPrefix + userId).notifyuser(NotificationTypes.DisConnected, userName, null); var cx = _dbContext.ChatConnection.SingleOrDefault(c => c.ConnectionId == Context.ConnectionId); if (cx != null) @@ -144,7 +167,7 @@ namespace Yavsc else _logger.LogError($"Could not remove user cx {Context.ConnectionId}"); } - Abort(); + _cxManager.Abort(Context.ConnectionId); return base.OnDisconnected(stopCalled); } @@ -179,12 +202,12 @@ namespace Yavsc public void Nick(string nickName) { var candidate = "?" + nickName; - if (ChatUserNames.Any(u => u.Value == candidate)) + if (_cxManager.IsConnected(candidate)) { Clients.Caller.notifyUser(NotificationTypes.ExistingUserName, nickName, "aborting"); return; } - ChatUserNames[Context.ConnectionId] = "?" + nickName; + _cxManager.SetUserName( Context.ConnectionId, candidate); } public void JoinAsync(string roomName) @@ -192,62 +215,36 @@ namespace Yavsc var info = Join(roomName); Clients.Caller.joint(info); } + bool IsPresent(string roomName, string userName) + { + return _cxManager.IsPresent(roomName, userName); + } public ChatRoomInfo Join(string roomName) { - var userName = ChatUserNames[Context.ConnectionId]; - var roomGroupName = Constants.HubGroupRomsPrefix + roomName; - + var roomGroupName = ChatHubConstants.HubGroupRomsPrefix + roomName; + var user = _cxManager.GetUserName(Context.ConnectionId); + Groups.Add(Context.ConnectionId, roomGroupName); ChatRoomInfo chanInfo; - if (Channels.ContainsKey(roomName)) - { - if (Channels.TryGetValue(roomName, out chanInfo)) - { - if (chanInfo.Users.ContainsKey(Context.ConnectionId)) - _logger.LogWarning("user already joint."); - else - { - chanInfo.Users.Add(Context.ConnectionId, userName); - Groups.Add(Context.ConnectionId, roomGroupName); - Clients.Caller.joint(chanInfo); - Clients.Group(Constants.HubGroupRomsPrefix + roomName).notifyRoom(NotificationTypes.UserJoin, roomName, userName); - return chanInfo; - } - return null; - } - else - { - _logger.LogError("room seemd to be avaible ... but we could get no info on it."); - Clients.Caller.notifyRoom(NotificationTypes.Error, roomName, "join get chan failed ..."); - return null; - } - } - var room = _dbContext.ChatRoom.FirstOrDefault(r => r.Name == roomName); - chanInfo = new ChatRoomInfo(); - chanInfo.Users.Add(Context.ConnectionId, userName); - - if (room != null) - { - chanInfo.Topic = room.Topic; - chanInfo.Name = room.Name; - } - else - { // a first join, we create it. - chanInfo.Name = roomName; - chanInfo.Topic = ""; - } - - if (Channels.TryAdd(roomName, chanInfo)) + if (!_cxManager.IsPresent(roomName, user)) { - Groups.Add(Context.ConnectionId, roomGroupName); - return (chanInfo); - } - else _logger.LogError("Chan create failed unexpectly..."); - return null; + chanInfo = _cxManager.Join(roomName, user); + Clients.Group(roomGroupName).notifyRoom(NotificationTypes.UserJoin, roomName, user); + } else{ + // in case in an additional connection, + // one only send info on room without + // warning any other user. + _cxManager.TryGetChanInfo(roomName, out chanInfo); + } + + // FIXME useless : Mobiles should also reveive the returned value + Clients.Caller.joint(chanInfo); + + return chanInfo; } [Authorize] - public void Register(string room) + public void Register([Required] string room) { var existent = _dbContext.ChatRoom.Any(r => r.Name == room); if (existent) @@ -257,9 +254,10 @@ namespace Yavsc } string userName = Context.User.Identity.Name; var user = _dbContext.Users.FirstOrDefault(u => u.UserName == userName); + var newroom = new ChatRoom { Name = room, OwnerId = user.Id }; ChatRoomInfo chanInfo; - if (Channels.TryGetValue(room, out chanInfo)) + if (_cxManager.TryGetChanInfo(room, out chanInfo)) { // TODO get and require some admin status for current user on this chan newroom.Topic = chanInfo.Topic; @@ -269,80 +267,87 @@ namespace Yavsc _dbContext.ChatRoom.Add(newroom); _dbContext.SaveChanges(user.Id); } - - /** TODO chan register on server command - room = new ChatRoom { Name = roomName, OwnerId = uid }; - _dbContext.ChatRoom.Add(room); - _dbContext.SaveChanges(uid); - room.LatestJoinPart = DateTime.Now; - chanInfo.Topic = room.Topic; - */ - - - public void Part(string roomName, string reason) + public void KickBan([Required] string roomName, [Required] string userName, [Required] string reason) + { + Kick(roomName, userName, reason); + Ban(roomName, userName, reason); + } + public void Kick([Required] string roomName, [Required] string userName, [Required] string reason) { ChatRoomInfo chanInfo; - if (Channels.TryGetValue(roomName, out chanInfo)) + var roomGroupName = ChatHubConstants.HubGroupRomsPrefix + roomName; + if (_cxManager.TryGetChanInfo(roomName, out chanInfo)) { - var roomGroupName = Constants.HubGroupRomsPrefix + roomName; - if (!chanInfo.Users.ContainsKey(Context.ConnectionId)) + if (!_cxManager.IsPresent(roomName,userName)) { - NotifyRoomError(roomName, "you didn't join."); + NotifyErrorToCallerInRoom(roomName, $"{userName} was not found in {roomName}."); return; } - Groups.Remove(Context.ConnectionId, roomGroupName); - var group = Clients.Group(roomGroupName); - var username = ChatUserNames[Context.ConnectionId]; - group.notifyRoom(NotificationTypes.UserPart, roomName, $"{username}: {reason}"); - - chanInfo.Users.Remove(Context.ConnectionId); - ChatRoomInfo deadchanInfo; - if (chanInfo.Users.Count == 0) - if (Channels.TryRemove(roomName, out deadchanInfo)) - { - var room = _dbContext.ChatRoom.FirstOrDefault(r => r.Name == roomName); - room.LatestJoinPart = DateTime.Now; - _dbContext.SaveChanges(); - } - } - else - { - NotifyRoomError(roomName, $"could not join: no such room"); + // in case of Kick returned false, being not allowed to, or for what ever other else failure, + // the error handler will send an error message while handling the error. + if (!_cxManager.Kick(Context.ConnectionId, userName, roomName, reason)) return; } + var ukeys = _cxManager.GetConnexionIds(userName); + + foreach(var ukey in ukeys) + Groups.Remove(ukey, roomGroupName); + Clients.Group(roomGroupName).notifyRoom(NotificationTypes.Kick, roomName, $"{userName}: {reason}"); } - void NotifyRoomError(string room, string reason) + public void Ban([Required] string roomName, [Required] string userName, [Required] string reason) { - Clients.Caller.notifyUser(NotificationTypes.Error, room, reason); + var cxIds = _cxManager.GetConnexionIds(userName); + throw new NotImplementedException(); + } + public void Gline([Required] string userName, [Required] string reason) + { + throw new NotImplementedException(); } + + public void Part([Required] string roomName, [Required] string reason) + { + if (_cxManager.Part(Context.ConnectionId, roomName, reason)) + { + var roomGroupName = ChatHubConstants.HubGroupRomsPrefix + roomName; + var group = Clients.Group(roomGroupName); + var userName = _cxManager.GetUserName(Context.ConnectionId); + group.notifyRoom(NotificationTypes.UserPart, roomName, $"{userName}: {reason}"); + Groups.Remove(Context.ConnectionId, roomGroupName); + } + else { + _logger.LogError("Could not part"); + } + } - public void Send(string roomName, string message) + void NotifyErrorToCallerInRoom(string room, string reason) { - var groupname = Constants.HubGroupRomsPrefix + roomName; - ChatRoomInfo chanInfo; - if (Channels.TryGetValue(roomName, out chanInfo)) + Clients.Caller.notifyUser(NotificationTypes.Error, room, reason); + } + + + public void Send([Required] string roomName, [Required] string message) + { + var groupname = ChatHubConstants.HubGroupRomsPrefix + roomName; + ChatRoomInfo chanInfo ; + if (!_cxManager.TryGetChanInfo(roomName, out chanInfo)) { - if (!chanInfo.Users.ContainsKey(Context.ConnectionId)) - { - var notSentMsg = $"could not send to channel ({roomName}) (not joint)"; - Clients.Caller.notifyUser(NotificationTypes.Error, roomName, notSentMsg); - return; - } - string uname = ChatUserNames[Context.ConnectionId]; - Clients.Group(groupname).addMessage(uname, roomName, message); + var noChanMsg = _localizer.GetString(ChatHubConstants.LabNoSuchChan); + Clients.Caller.notifyUser(NotificationTypes.Error, roomName, noChanMsg); + return; } - else + var userName = _cxManager.GetUserName(Context.ConnectionId); + if (!chanInfo.Users.Contains(userName)) { - var noChanMsg = $"could not send to channel ({roomName}) (no such chan)"; - Clients.Caller.notifyUser(NotificationTypes.Error, roomName, noChanMsg); + var notSentMsg = _localizer.GetString(ChatHubConstants.LabnoJoinNoSend); + Clients.Caller.notifyUser(NotificationTypes.Error, roomName, notSentMsg); return; } - + Clients.Group(groupname).addMessage(userName, roomName, message); } [Authorize] - public void SendPV(string userName, string message) + public void SendPV([Required] string userName, [Required] string message) { if (string.IsNullOrWhiteSpace(userName)) { @@ -365,7 +370,7 @@ namespace Yavsc return; } } - var cxIds = ChatUserNames.Where(name => name.Value == userName).Select(name => name.Key); + var cxIds = _cxManager.GetConnexionIds(userName); foreach (var connectionId in cxIds) { @@ -376,19 +381,13 @@ namespace Yavsc [Authorize] - public void SendStream(string connectionId, long streamId, string message) + public void SendStream([Required] string connectionId, long streamId, [Required] string message) { var sender = Context.User.Identity.Name; var cli = Clients.Client(connectionId); cli.addStreamInfo(sender, streamId, message); } - void Abort() - { - string cxId; - if (!ChatUserNames.TryRemove(Context.ConnectionId, out cxId)) - _logger.LogError($"Could not remove user cx {Context.ConnectionId}"); - - } + } } diff --git a/src/Yavsc/Hubs/ChatRoomInfo.cs b/src/Yavsc/Hubs/ChatRoomInfo.cs index ddad624b..ff17d848 100644 --- a/src/Yavsc/Hubs/ChatRoomInfo.cs +++ b/src/Yavsc/Hubs/ChatRoomInfo.cs @@ -23,13 +23,12 @@ using System.Collections.Generic; namespace Yavsc { - public partial class ChatHub + public class ChatRoomInfo { - public class ChatRoomInfo - { - public string Name; - public Dictionary Users = new Dictionary(); - public string Topic; - } + public string Name; + public List Users = new List(); + public List Ops = new List(); + public List Hops = new List(); + public string Topic; } } diff --git a/src/Yavsc/Resources/Yavsc.ChatHub.en.resx b/src/Yavsc/Resources/Yavsc.ChatHub.en.resx new file mode 100644 index 00000000..e5cd70f1 --- /dev/null +++ b/src/Yavsc/Resources/Yavsc.ChatHub.en.resx @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Authenticated chat user + could not send to channel (not joint) + \ No newline at end of file diff --git a/src/Yavsc/Resources/Yavsc.ChatHub.fr.resx b/src/Yavsc/Resources/Yavsc.ChatHub.fr.resx new file mode 100644 index 00000000..a878031d --- /dev/null +++ b/src/Yavsc/Resources/Yavsc.ChatHub.fr.resx @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Utilisateur de chat authentifié + Envoi impossible: vous devez joindre le canal pour y contribuer. + \ No newline at end of file diff --git a/src/Yavsc/Resources/Yavsc.Services.ChatHubConnectionManager.fr.resx b/src/Yavsc/Resources/Yavsc.Services.ChatHubConnectionManager.fr.resx new file mode 100644 index 00000000..a0146cfb --- /dev/null +++ b/src/Yavsc/Resources/Yavsc.Services.ChatHubConnectionManager.fr.resx @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Vient d'être créé par + pas de tel utilisateur + Vous n'êtes pas op. + \ No newline at end of file diff --git a/src/Yavsc/Services/ChatHubConnexionManager.cs b/src/Yavsc/Services/ChatHubConnexionManager.cs new file mode 100644 index 00000000..cf0c30f4 --- /dev/null +++ b/src/Yavsc/Services/ChatHubConnexionManager.cs @@ -0,0 +1,333 @@ + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.Windows.Input; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Localization; +using Microsoft.Extensions.Logging; +using Yavsc.Abstract.Chat; +using Yavsc.Models; +using Yavsc.ViewModels.Chat; + +namespace Yavsc.Services +{ + + /// + /// Connexion Manager + /// + public class HubConnectionManager : IConnexionManager + { + ILogger _logger; + + Action _errorHandler; + + /// + /// by cx id + /// + /// + /// + /// + + static ConcurrentDictionary ChatUserNames = new ConcurrentDictionary(); + /// + /// by user name + /// + /// + static ConcurrentDictionary> ChatCxIds = new ConcurrentDictionary>(); + + /// + /// by user name + /// + /// + static ConcurrentDictionary> ChatRoomPresence = new ConcurrentDictionary>(); + static ConcurrentDictionary _isCop = new ConcurrentDictionary(); + + public static ConcurrentDictionary Channels = new ConcurrentDictionary(); + private ApplicationDbContext _dbContext; + private IStringLocalizer _localizer; + + public HubConnectionManager() + { + var scope = Startup.Services.GetRequiredService().CreateScope(); + _dbContext = scope.ServiceProvider.GetService(); + var loggerFactory = scope.ServiceProvider.GetService(); + _logger = loggerFactory.CreateLogger(); + var stringLocFactory = scope.ServiceProvider.GetService(); + _localizer = stringLocFactory.Create(typeof(HubConnectionManager)); + } + + public void SetUserName(string cxId, string userName) + { + string oldUname; + if (ChatUserNames.TryGetValue(cxId, out oldUname)) + { + // this is a rename + if (oldUname == userName) return; + ChatCxIds[userName] = ChatCxIds[oldUname]; + ChatCxIds[oldUname] = null; + } + else + { + // this is a connexion + ChatCxIds[userName] = new List() { cxId }; + } + ChatUserNames[cxId] = userName; + } + + public void OnConnected(string userName, bool isCop) + { + ChatRoomPresence[userName] = new List(); + _isCop[userName] = isCop; + } + + public bool IsConnected(string candidate) + { + return ChatRoomPresence[candidate] != null; + } + + public bool IsPresent(string roomName, string userName) + { + return ChatRoomPresence[userName].Contains(roomName); + } + + public bool isCop(string userName) + { + return _isCop[userName]; + } + public void Abort(string connectionId) + { + string uname; + + if (!ChatUserNames.TryRemove(connectionId, out uname)) + _logger.LogError($"Could not remove user name for cx {connectionId}"); + else + { + List cxIds; + if (ChatCxIds.TryGetValue(uname, out cxIds)) + { + cxIds.Remove(connectionId); + } + else + _logger.LogError($"Could not remove user cx {connectionId}"); + + foreach (var room in ChatRoomPresence[uname]) + { + Part(connectionId, room, "connexion aborted"); + } + ChatRoomPresence[uname] = null; + } + } + + public bool Part(string cxId, string roomName, string reason) + { + ChatRoomInfo chanInfo; + var userName = ChatUserNames[cxId]; + if (Channels.TryGetValue(roomName, out chanInfo)) + { + if (!chanInfo.Users.Contains(userName)) + { + // TODO NotifyErrorToCaller(roomName, "you didn't join."); + return false; + } + chanInfo.Users.Remove(userName); + if (chanInfo.Users.Count == 0) + { + ChatRoomInfo deadchanInfo; + if (Channels.TryRemove(roomName, out deadchanInfo)) + { + var room = _dbContext.ChatRoom.FirstOrDefault(r => r.Name == roomName); + room.LatestJoinPart = DateTime.Now; + _dbContext.SaveChanges(); + } + } + return true; + } + else + { + return false; + // TODO NotifyErrorToCallerInRoom(roomName, $"user could not part: no such room"); + } + } + + public ChatRoomInfo Join(string roomName, string userName) + { + _logger.LogInformation($"Join: {userName}=>{roomName}"); + ChatRoomInfo chanInfo; + // if channel already is open + if (Channels.ContainsKey(roomName)) + { + if (Channels.TryGetValue(roomName, out chanInfo)) + { + if (IsPresent(roomName, userName)) + { + // TODO implement some unique connection sharing protocol + // between all terminals from a single user. + return chanInfo; + } + else + { + if (isCop(userName)) + { + chanInfo.Ops.Add(userName); + } + else{ + chanInfo.Users.Add(userName); + } + _logger.LogInformation($"existing room joint: {userName}=>{roomName}"); + ChatRoomPresence[userName].Add(roomName); + return chanInfo; + } + } + else + { + string msg = "room seemd to be avaible ... but we could get no info on it."; + _errorHandler(roomName, msg); + _logger.LogError(msg); + return null; + } + } + // room was closed. + var room = _dbContext.ChatRoom.FirstOrDefault(r => r.Name == roomName); + chanInfo = new ChatRoomInfo(); + + + if (room != null) + { + chanInfo.Topic = room.Topic; + chanInfo.Name = room.Name; + chanInfo.Users.Add(userName); + } + else + { // a first join, we create it. + chanInfo.Name = roomName; + chanInfo.Topic = _localizer.GetString(ChatHubConstants.JustCreatedBy)+userName; + chanInfo.Ops.Add(userName); + } + + if (Channels.TryAdd(roomName, chanInfo)) + { + _logger.LogInformation("new room joint"); + return (chanInfo); + } + else + { + string msg = "Chan create failed unexpectly..."; + _errorHandler(roomName, msg); + _logger.LogError(msg); + return null; + } + } + + public bool Op(string roomName, string userName) + { + throw new System.NotImplementedException(); + } + + public bool Deop(string roomName, string userName) + { + throw new System.NotImplementedException(); + } + + public bool Hop(string roomName, string userName) + { + throw new System.NotImplementedException(); + } + + public bool Dehop(string roomName, string userName) + { + throw new System.NotImplementedException(); + } + + public string GetUserName(string cxId) + { + return ChatUserNames[cxId]; + } + + public bool TryGetChanInfo(string room, out ChatRoomInfo chanInfo) + { + return Channels.TryGetValue(room, out chanInfo); + } + + public IEnumerable ListChannels(string pattern) + { + if (pattern != null) + return Channels.Where(c => c.Key.Contains(pattern)) + .OrderByDescending(c => c.Value.Users.Count).Select(c => new ChannelShortInfo { RoomName = c.Key, Topic = c.Value.Topic }).Take(10); + + return Channels + .OrderByDescending(c => c.Value.Users.Count).Select(c => new ChannelShortInfo { RoomName = c.Key, Topic = c.Value.Topic }).Take(10); + } + + public IEnumerable GetConnexionIds(string userName) + { + return ChatCxIds[userName]; + } + + /// + /// set on error as string couple action + /// + /// + public void SetErrorHandler(Action errorHandler) + { + _errorHandler = errorHandler; + } + + public bool Kick(string cxId, string userName, string roomName, string reason) + { + ChatRoomInfo chanInfo; + if (!Channels.ContainsKey(roomName)) + { + _errorHandler(roomName, _localizer.GetString(ChatHubConstants.LabNoSuchChan)); + return false; + } + + if (!Channels.TryGetValue(roomName, out chanInfo)) + { + _errorHandler(roomName, _localizer.GetString(ChatHubConstants.LabNoSuchChan)); + return false; + } + + var kickerName = GetUserName(cxId); + if (!chanInfo.Ops.Contains(kickerName)) + if (!chanInfo.Hops.Contains(kickerName)) + { + _errorHandler(roomName, _localizer.GetString(ChatHubConstants.LabYouNotOp)); + return false; + } + + if (!IsPresent(roomName, userName)) + { + _errorHandler(roomName, _localizer.GetString(ChatHubConstants.LabNoSuchUser)); + return false; + } + if (chanInfo.Hops.Contains(kickerName)) + if (chanInfo.Ops.Contains(userName)) + { + _errorHandler(roomName, _localizer.GetString(ChatHubConstants.HopWontKickOp)); + return false; + } + if (isCop(userName)) + { + _errorHandler(roomName, _localizer.GetString(ChatHubConstants.NoKickOnCop)); + return false; + } + + // all good, time to kick :-) + + if (chanInfo.Users.Contains(userName)) + chanInfo.Users.Remove(userName); + + else if (chanInfo.Ops.Contains(userName)) + chanInfo.Ops.Remove(userName); + + else if (chanInfo.Hops.Contains(userName)) + chanInfo.Hops.Remove(userName); + + return true; + + } + } +} diff --git a/src/Yavsc/Services/IConnexionManager.cs b/src/Yavsc/Services/IConnexionManager.cs new file mode 100644 index 00000000..4add5fc8 --- /dev/null +++ b/src/Yavsc/Services/IConnexionManager.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using Yavsc.ViewModels.Chat; + +namespace Yavsc.Services +{ + public interface IConnexionManager { + void SetUserName(string cxId, string userName); + + string GetUserName (string cxId); + void OnConnected(string userName, bool isCop); + bool IsConnected(string candidate); + bool IsPresent(string roomName, string userName); + + ChatRoomInfo Join(string roomName, string userName); + + bool Part(string cxId, string roomName, string reason); + + bool Kick(string cxId, string userName, string roomName, string reason); + + bool Op(string roomName, string userName); + bool Deop(string roomName, string userName); + bool Hop(string roomName, string userName); + bool Dehop(string roomName, string userName); + bool TryGetChanInfo(string room, out ChatRoomInfo chanInfo); + + IEnumerable GetConnexionIds(string userName); + void Abort(string connectionId); + + void SetErrorHandler(Action errorHandler); + IEnumerable ListChannels(string pattern); + + } +} diff --git a/src/Yavsc/Services/YavscMessageSender.cs b/src/Yavsc/Services/YavscMessageSender.cs index 5bef57ee..af0154dd 100644 --- a/src/Yavsc/Services/YavscMessageSender.cs +++ b/src/Yavsc/Services/YavscMessageSender.cs @@ -22,12 +22,15 @@ namespace Yavsc.Services private IHubContext hubContext; ApplicationDbContext _dbContext; + IConnexionManager _cxManager; + public YavscMessageSender( ILoggerFactory loggerFactory, IOptions sitesOptions, IOptions smtpOptions, IEmailSender emailSender, - ApplicationDbContext dbContext + ApplicationDbContext dbContext, + IConnexionManager cxManager ) { _logger = loggerFactory.CreateLogger(); @@ -35,7 +38,9 @@ namespace Yavsc.Services siteSettings = sitesOptions?.Value; hubContext = GlobalHost.ConnectionManager.GetHubContext(); _dbContext = dbContext; + _cxManager = cxManager; } + public async Task NotifyEvent (IEnumerable userIds, Event ev) where Event : IEvent @@ -82,7 +87,7 @@ namespace Yavsc.Services var body = ev.CreateBody(); - var cxids = ChatHub.ChatUserNames.Where (kv=>kv.Value == user.UserName).Select(kv => kv.Key).ToArray(); + var cxids = _cxManager.GetConnexionIds(user.UserName).ToArray(); if (cxids.Length==0) { diff --git a/src/Yavsc/Startup/Startup.cs b/src/Yavsc/Startup/Startup.cs index db811c97..22500a7d 100755 --- a/src/Yavsc/Startup/Startup.cs +++ b/src/Yavsc/Startup/Startup.cs @@ -135,7 +135,8 @@ namespace Yavsc services.Add(ServiceDescriptor.Singleton(typeof(IOptions), typeof(OptionsManager))); services.Add(ServiceDescriptor.Singleton(typeof(IOptions), typeof(OptionsManager))); services.Add(ServiceDescriptor.Singleton(typeof(IOptions), typeof(OptionsManager))); - + + services.Configure(options => { var supportedCultures = new[] @@ -240,6 +241,7 @@ namespace Yavsc services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); services.AddMvc(config => { @@ -398,7 +400,7 @@ namespace Yavsc } } // before fixing the security protocol, let beleive our lib it's done with it. - var cxmgr = ConnectionManager.Instance; + var cxmgr = PayPal.Manager.ConnectionManager.Instance; // then, fix it. ServicePointManager.SecurityProtocol = (SecurityProtocolType)0xC00; // Tls12, required by PayPal diff --git a/src/Yavsc/Views/GCMDevices/Delete.cshtml b/src/Yavsc/Views/Devices/Delete.cshtml similarity index 100% rename from src/Yavsc/Views/GCMDevices/Delete.cshtml rename to src/Yavsc/Views/Devices/Delete.cshtml diff --git a/src/Yavsc/Views/GCMDevices/Details.cshtml b/src/Yavsc/Views/Devices/Details.cshtml similarity index 100% rename from src/Yavsc/Views/GCMDevices/Details.cshtml rename to src/Yavsc/Views/Devices/Details.cshtml diff --git a/src/Yavsc/Views/GCMDevices/Index.cshtml b/src/Yavsc/Views/Devices/Index.cshtml similarity index 80% rename from src/Yavsc/Views/GCMDevices/Index.cshtml rename to src/Yavsc/Views/Devices/Index.cshtml index 425af934..41c5ae98 100644 --- a/src/Yavsc/Views/GCMDevices/Index.cshtml +++ b/src/Yavsc/Views/Devices/Index.cshtml @@ -1,4 +1,4 @@ -@model IEnumerable +@model IEnumerable @{ ViewData["Title"] = "Index"; @@ -11,9 +11,6 @@ @Html.DisplayNameFor(model => model.DeclarationDate) - - @Html.DisplayNameFor(model => model.GCMRegistrationId) - @Html.DisplayNameFor(model => model.Model) @@ -31,9 +28,6 @@ @Html.DisplayFor(modelItem => item.DeclarationDate) - - @Html.DisplayFor(modelItem => item.GCMRegistrationId) - @Html.DisplayFor(modelItem => item.Model) diff --git a/src/Yavsc/Views/Shared/_Layout.cshtml b/src/Yavsc/Views/Shared/_Layout.cshtml index 41c17b7a..1a6dfe5e 100644 --- a/src/Yavsc/Views/Shared/_Layout.cshtml +++ b/src/Yavsc/Views/Shared/_Layout.cshtml @@ -136,7 +136,7 @@ -

Yavsc - Copyright © 2015 - 2018 Paul Schneider

+

Yavsc - Copyright © 2015 - 2019 Paul Schneider

@RenderSection("scripts", required: false) diff --git a/src/Yavsc/omnisharp.json b/src/Yavsc/omnisharp.json deleted file mode 100644 index 10ffb10f..00000000 --- a/src/Yavsc/omnisharp.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "Dnx": { - "enabled": true, - "enablePackageRestore": false, - "projects": "../../src/**/project.json;../../test/**/project.json" - }, - "MSBuild": { - "enabled": false - }, - "DotNet": { - "enabled": false, - "enablePackageRestore": false, - "script": { - "enableScriptNuGetReferences": true, - "defaultTargetFramework": "dnx451" - } - }, - "packages": "packages" -} diff --git a/src/Yavsc/wwwroot/js/chat.js b/src/Yavsc/wwwroot/js/chat.js index b6135b75..963b1900 100644 --- a/src/Yavsc/wwwroot/js/chat.js +++ b/src/Yavsc/wwwroot/js/chat.js @@ -245,7 +245,7 @@ window.ChatHubHandler = (function ($) { var addChatUser = function (uname) { - $('#u' + uname).remove(); + $('#u_' + uname).remove(); // ulist.remove("li.user[data='"+uname+"']"); $('
  • ' + uname + '
  • ') diff --git a/src/omnisharp.log b/src/omnisharp.log new file mode 100644 index 00000000..9fed03db --- /dev/null +++ b/src/omnisharp.log @@ -0,0 +1 @@ +["-s","/home/paul/workspace/yavsc/src/Yavsc","--hostPID","7920","DotNet:enablePackageRestore=false","--encoding","utf-8","--loglevel","warning","FileOptions:SystemExcludeSearchPatterns:0=**/.git","FileOptions:SystemExcludeSearchPatterns:1=**/.svn","FileOptions:SystemExcludeSearchPatterns:2=**/.hg","FileOptions:SystemExcludeSearchPatterns:3=**/CVS","FileOptions:SystemExcludeSearchPatterns:4=**/.DS_Store"]Store","FileOptions:SystemExcludeSearchPatterns:5=**/bin","FileOptions:SystemExcludeSearchPatterns:6=**/build","FileOptions:SystemExcludeSearchPatterns:7=**/wrap"] \ No newline at end of file diff --git a/src/test/WIP/YavscWorkInProgress.cs b/src/test/Mandatory/Remoting.cs similarity index 100% rename from src/test/WIP/YavscWorkInProgress.cs rename to src/test/Mandatory/Remoting.cs