diff --git a/MBBSEmu/HostProcess/GlobalRoutines/SysopGlobal.cs b/MBBSEmu/HostProcess/GlobalRoutines/SysopGlobal.cs index df30a164..a5a07409 100644 --- a/MBBSEmu/HostProcess/GlobalRoutines/SysopGlobal.cs +++ b/MBBSEmu/HostProcess/GlobalRoutines/SysopGlobal.cs @@ -246,11 +246,7 @@ private void RemoveAccount(IReadOnlyList commandSequence) //Remove the User from the BBSUSR Database var accountBtrieve = _globalCache.Get("ACCBB-PROCESSOR"); - var result = accountBtrieve.PerformOperation(0, new Span(new UserAccount - { - userid = Encoding.ASCII.GetBytes(userAccount.userName.ToUpper()), - psword = Encoding.ASCII.GetBytes("<>") - }.Data).Slice(0, 55), EnumBtrieveOperationCodes.AcquireEqual); + var result = accountBtrieve.PerformOperation(0, new Span(new UserAccount(userAccount.userName.ToUpper()).Data)[..55], EnumBtrieveOperationCodes.AcquireEqual); if (result) accountBtrieve.Delete(); @@ -287,11 +283,7 @@ private void ChangeSex(IReadOnlyList commandSequence) //Remove the User from the BBSUSR.db Database var accountBtrieve = _globalCache.Get("ACCBB-PROCESSOR"); - var result = accountBtrieve.PerformOperation(0, new Span(new UserAccount - { - userid = Encoding.ASCII.GetBytes(userName.ToUpper()), - psword = Encoding.ASCII.GetBytes("<>") - }.Data).Slice(0, 55), EnumBtrieveOperationCodes.AcquireEqual); + var result = accountBtrieve.PerformOperation(0, new Span(new UserAccount(userName.ToUpper()).Data)[..55], EnumBtrieveOperationCodes.AcquireEqual); if (!result) { diff --git a/MBBSEmu/HostProcess/HostRoutines/MenuRoutines.cs b/MBBSEmu/HostProcess/HostRoutines/MenuRoutines.cs index d0c8ce34..4fb536ff 100644 --- a/MBBSEmu/HostProcess/HostRoutines/MenuRoutines.cs +++ b/MBBSEmu/HostProcess/HostRoutines/MenuRoutines.cs @@ -251,11 +251,7 @@ private void LoginPasswordInput(SessionBase session) //Lookup User in BBSUSR.db var accountBtrieve = _globalCache.Get("ACCBB-PROCESSOR"); - var result = accountBtrieve.PerformOperation(0, new Span(new UserAccount - { - userid = Encoding.ASCII.GetBytes(session.Username.ToUpper()), - psword = Encoding.ASCII.GetBytes("<>") - }.Data).Slice(0, 55), EnumBtrieveOperationCodes.AcquireEqual); + var result = accountBtrieve.PerformOperation(0, new Span(new UserAccount(session.Username.ToUpper()).Data)[..55], EnumBtrieveOperationCodes.AcquireEqual); if (!result) { @@ -605,7 +601,7 @@ private void SignupGenderInput(SessionBase session) //Add The User to the BBS Btrieve User Database var _accountBtrieve = _globalCache.Get("ACCBB-PROCESSOR"); - _accountBtrieve.Insert(new UserAccount { userid = Encoding.ASCII.GetBytes(session.Username), psword = Encoding.ASCII.GetBytes("<>"), sex = session.UsrAcc.sex }.Data, LogLevel.Error); + _accountBtrieve.Insert(new UserAccount(session.Username, (char)session.UsrAcc.sex).Data, LogLevel.Error); session.SessionState = EnumSessionState.LoginRoutines; session.InputBuffer.SetLength(0); diff --git a/MBBSEmu/HostProcess/Structs/UserAccount.cs b/MBBSEmu/HostProcess/Structs/UserAccount.cs index de933eee..3490a973 100644 --- a/MBBSEmu/HostProcess/Structs/UserAccount.cs +++ b/MBBSEmu/HostProcess/Structs/UserAccount.cs @@ -376,6 +376,28 @@ public byte[] birthd public const ushort Size = 338; + /// + /// Constructor for UserAccount where you can specify the User's UserName and Sex via constructor parameters + /// + /// Username the user will use to log into the system + /// Sex/Gender of the User (Only M/F supported) + public UserAccount(string userName, char userSex = 'M') : this() + { + //Verify Input Parameters + if (userName.Length > UIDSIZ - 1) + throw new ArgumentOutOfRangeException(nameof(userName), $"Username must be {UIDSIZ - 1} characters or less"); + + if (userSex != 'M' && userSex != 'F') + throw new ArgumentOutOfRangeException(nameof(userSex), "Only M or F are supported for userSex in The MajorBBS/Worldgroup"); + + userid = Encoding.ASCII.GetBytes(userName + "\0"); + psword = Encoding.ASCII.GetBytes("<>"); //Password is always hashed in the internal database, so we don't store it here as the hashed value would be too long + sex = (byte)userSex; + } + + /// + /// Default Constructor for UserAccount + /// public UserAccount() { flags = 1; //Set everyone to having "MASTER" key diff --git a/MBBSEmu/Program.cs b/MBBSEmu/Program.cs index e68e5399..9996e4c2 100644 --- a/MBBSEmu/Program.cs +++ b/MBBSEmu/Program.cs @@ -90,6 +90,16 @@ public class Program /// private string _newSysopPassword; + /// + /// Specified if the -DBREBUILD Command Line Argument was passed + /// + private bool _doDatabaseRebuild; + + /// + /// Database File to be Rebuilt (if able) + /// + private string _databaseRebuildFileName; + /// /// Specified if the -CONSOLE Command Line Argument was passed /// @@ -177,6 +187,18 @@ private void Run(string[] args) break; } + case "-DBREBUILD": + { + _doDatabaseRebuild = true; + if (i + 1 < args.Length && args[i + 1][0] != '-') + { + _databaseRebuildFileName = args[i + 1]; + i++; + } + + _databaseRebuildFileName = _databaseRebuildFileName.ToUpperInvariant(); + break; + } case "-APIREPORT": _doApiReport = true; break; @@ -349,6 +371,20 @@ private void Run(string[] args) DatabaseReset(); } + //Database Rebuild + if (_doDatabaseRebuild) + { + switch (_databaseRebuildFileName) + { + case "BBSUSR": + RebuildAccDb(); + break; + default: + _logger.Error($"Unknown Database to rebuild: {_databaseRebuildFileName}"); + break; + } + } + //Setup Modules if (!string.IsNullOrEmpty(_moduleIdentifier)) { @@ -568,8 +604,8 @@ private void DatabaseReset() //Insert Into BBS Account Btrieve File var _accountBtrieve = _serviceResolver.GetService().Get("ACCBB-PROCESSOR"); _accountBtrieve.DeleteAll(); - _accountBtrieve.Insert(new UserAccount { userid = Encoding.ASCII.GetBytes("sysop"), psword = Encoding.ASCII.GetBytes("<>"), sex = (byte)'M' }.Data, LogLevel.Error); - _accountBtrieve.Insert(new UserAccount { userid = Encoding.ASCII.GetBytes("guest"), psword = Encoding.ASCII.GetBytes("<>"), sex = (byte)'M' }.Data, LogLevel.Error); + _accountBtrieve.Insert(new UserAccount("sysop").Data, LogLevel.Error); + _accountBtrieve.Insert(new UserAccount("guest").Data, LogLevel.Error); //Reset BBSGEN var _genbbBtrieve = _serviceResolver.GetService().Get("GENBB-PROCESSOR"); @@ -617,5 +653,49 @@ private void PasswordReset() _logger.Info("Sysop Password Reset!"); _doResetPassword = false; } + + /// + /// Rebuilds the internal MajorBBS/WG Account Database (BBSUSR.DAT) using the current SQLite Database + /// and users that are already created. This is useful if you have a corrupted or missing BBSUSR.DB. + /// + /// The BBSUSR.DAT file (ACCDB) is used by MajorBBS/WG to store user accounts. It is referenced by several + /// internal API calls and is required for the system to function properly. Because of this, we only store the + /// bare minimum amount of inofrmation required for this file to exist and be valid. Full user account information + /// is stored within the internal MBBSEmu SQLite Database. + /// + /// This might seem a little confusing, but internally in the MajorBBS code this database is referenced + /// as "ACCDB" but the actual file name is BBSUSR.DAT. + /// + private void RebuildAccDb() + { + _logger.Info("Rebuilding BBSUSR.DAT..."); + + //Get Internal MBBSEmu User Account Database + var acct = _serviceResolver.GetService(); + var accounts = acct.GetAccounts(); + + //Verify there are valid accounts in the MBBSEmu Accounts Database + if (!accounts.Any()) + { + _logger.Error("No Accounts Found in MBBSEmu Database, skipping rebuild of BBSUSR.DAT"); + _logger.Error("Please consider using the -DBRESET command line argument to reset the internal databases to their default state"); + return; + } + + //Get BBSUSR.DAT and clear out existing records + var _accountBtrieve = _serviceResolver.GetService().Get("ACCBB-PROCESSOR"); + _accountBtrieve.DeleteAll(); + + //Insert each record into BBSUSR.DAT + foreach (var a in accounts) + _accountBtrieve.Insert(new UserAccount(a.userName).Data, LogLevel.Error); + + //Verify the Counts are Equal + if (accounts.Count() != _accountBtrieve.GetRecordCount()) + _logger.Warn($"MBBSEmu Database Account Count ({accounts.Count()}) does not match BBSUSR.DAT Account Count ({_accountBtrieve.GetRecordCount()})"); + + _logger.Info("BBSUSR.DAT (BBSUSR.DB) Rebuilt!"); + _logger.Info($"{accounts.Count()} Accounts Inserted"); + } } }