Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add -DBREBUILD Command #611

Merged
merged 2 commits into from
Dec 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 2 additions & 10 deletions MBBSEmu/HostProcess/GlobalRoutines/SysopGlobal.cs
Original file line number Diff line number Diff line change
Expand Up @@ -246,11 +246,7 @@ private void RemoveAccount(IReadOnlyList<string> commandSequence)
//Remove the User from the BBSUSR Database
var accountBtrieve = _globalCache.Get<BtrieveFileProcessor>("ACCBB-PROCESSOR");

var result = accountBtrieve.PerformOperation(0, new Span<byte>(new UserAccount
{
userid = Encoding.ASCII.GetBytes(userAccount.userName.ToUpper()),
psword = Encoding.ASCII.GetBytes("<<HASHED>>")
}.Data).Slice(0, 55), EnumBtrieveOperationCodes.AcquireEqual);
var result = accountBtrieve.PerformOperation(0, new Span<byte>(new UserAccount(userAccount.userName.ToUpper()).Data)[..55], EnumBtrieveOperationCodes.AcquireEqual);

if (result)
accountBtrieve.Delete();
Expand Down Expand Up @@ -287,11 +283,7 @@ private void ChangeSex(IReadOnlyList<string> commandSequence)
//Remove the User from the BBSUSR.db Database
var accountBtrieve = _globalCache.Get<BtrieveFileProcessor>("ACCBB-PROCESSOR");

var result = accountBtrieve.PerformOperation(0, new Span<byte>(new UserAccount
{
userid = Encoding.ASCII.GetBytes(userName.ToUpper()),
psword = Encoding.ASCII.GetBytes("<<HASHED>>")
}.Data).Slice(0, 55), EnumBtrieveOperationCodes.AcquireEqual);
var result = accountBtrieve.PerformOperation(0, new Span<byte>(new UserAccount(userName.ToUpper()).Data)[..55], EnumBtrieveOperationCodes.AcquireEqual);

if (!result)
{
Expand Down
8 changes: 2 additions & 6 deletions MBBSEmu/HostProcess/HostRoutines/MenuRoutines.cs
Original file line number Diff line number Diff line change
Expand Up @@ -251,11 +251,7 @@ private void LoginPasswordInput(SessionBase session)
//Lookup User in BBSUSR.db
var accountBtrieve = _globalCache.Get<BtrieveFileProcessor>("ACCBB-PROCESSOR");

var result = accountBtrieve.PerformOperation(0, new Span<byte>(new UserAccount
{
userid = Encoding.ASCII.GetBytes(session.Username.ToUpper()),
psword = Encoding.ASCII.GetBytes("<<HASHED>>")
}.Data).Slice(0, 55), EnumBtrieveOperationCodes.AcquireEqual);
var result = accountBtrieve.PerformOperation(0, new Span<byte>(new UserAccount(session.Username.ToUpper()).Data)[..55], EnumBtrieveOperationCodes.AcquireEqual);

if (!result)
{
Expand Down Expand Up @@ -605,7 +601,7 @@ private void SignupGenderInput(SessionBase session)

//Add The User to the BBS Btrieve User Database
var _accountBtrieve = _globalCache.Get<BtrieveFileProcessor>("ACCBB-PROCESSOR");
_accountBtrieve.Insert(new UserAccount { userid = Encoding.ASCII.GetBytes(session.Username), psword = Encoding.ASCII.GetBytes("<<HASHED>>"), 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);
Expand Down
22 changes: 22 additions & 0 deletions MBBSEmu/HostProcess/Structs/UserAccount.cs
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,28 @@ public byte[] birthd

public const ushort Size = 338;

/// <summary>
/// Constructor for UserAccount where you can specify the User's UserName and Sex via constructor parameters
/// </summary>
/// <param name="userName">Username the user will use to log into the system</param>
/// <param name="userSex">Sex/Gender of the User (Only M/F supported)</param>
public UserAccount(string userName, char userSex = 'M') : this()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably best to use an enum for userSex parameter to make it a compile time error rather than a runtime error, but not a huge thing

{
//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("<<HASHED>>"); //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;
}

/// <summary>
/// Default Constructor for UserAccount
/// </summary>
public UserAccount()
{
flags = 1; //Set everyone to having "MASTER" key
Expand Down
84 changes: 82 additions & 2 deletions MBBSEmu/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,16 @@ public class Program
/// </summary>
private string _newSysopPassword;

/// <summary>
/// Specified if the -DBREBUILD Command Line Argument was passed
/// </summary>
private bool _doDatabaseRebuild;

/// <summary>
/// Database File to be Rebuilt (if able)
/// </summary>
private string _databaseRebuildFileName;

/// <summary>
/// Specified if the -CONSOLE Command Line Argument was passed
/// </summary>
Expand Down Expand Up @@ -177,6 +187,18 @@ private void Run(string[] args)

break;
}
case "-DBREBUILD":
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

command line parsing is getting crazy. I wrote some cool args/flags parsing code for Java, perhaps I should port it....

{
_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;
Expand Down Expand Up @@ -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))
{
Expand Down Expand Up @@ -568,8 +604,8 @@ private void DatabaseReset()
//Insert Into BBS Account Btrieve File
var _accountBtrieve = _serviceResolver.GetService<IGlobalCache>().Get<BtrieveFileProcessor>("ACCBB-PROCESSOR");
_accountBtrieve.DeleteAll();
_accountBtrieve.Insert(new UserAccount { userid = Encoding.ASCII.GetBytes("sysop"), psword = Encoding.ASCII.GetBytes("<<HASHED>>"), sex = (byte)'M' }.Data, LogLevel.Error);
_accountBtrieve.Insert(new UserAccount { userid = Encoding.ASCII.GetBytes("guest"), psword = Encoding.ASCII.GetBytes("<<HASHED>>"), 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<IGlobalCache>().Get<BtrieveFileProcessor>("GENBB-PROCESSOR");
Expand Down Expand Up @@ -617,5 +653,49 @@ private void PasswordReset()
_logger.Info("Sysop Password Reset!");
_doResetPassword = false;
}

/// <summary>
/// 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.
/// </summary>
private void RebuildAccDb()
{
_logger.Info("Rebuilding BBSUSR.DAT...");

//Get Internal MBBSEmu User Account Database
var acct = _serviceResolver.GetService<IAccountRepository>();
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<IGlobalCache>().Get<BtrieveFileProcessor>("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()})");
Comment on lines +694 to +695
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this likely isn't necessary since Insert should throw if unable to insert a record


_logger.Info("BBSUSR.DAT (BBSUSR.DB) Rebuilt!");
_logger.Info($"{accounts.Count()} Accounts Inserted");
}
}
}
Loading