unit hsiNetscapeRegistry;

{Note that this implementation doesn't deal with data types other than strings,
because that was all we needed for our purposes (identifying the location of
the Netscape cert7/8, key3 and secmod files). This could readily be added.}

interface

uses
  Windows, Classes, Sysutils;

type
  TRegDataType = (
    rdtKey,
    rdtValue);

  PRegEntry = ^TRegEntry;
  TRegEntry = record
    dwLocation: DWord;
    dwNamePtr: DWord;
    wNameLen: Word;
    wType: Word;
    dwLeft: DWord;
    dwDown: DWord;
    dwValuePtr: DWord;
    dwValueLen: DWord;
    dwParent: DWord;
  end;

  PDataRec = ^TDataRec;
  TDataRec = record
    Parent: PDataRec;
    DataType: TRegDataType;
    Name: String;
    Value: String;
    Children: TList;
  end;

  TNSRegistry = class(TObject)
  private
    FStream: TFileStream;
    FRootData: PDataRec;
    procedure ReadRegistry(const ARegFile: String);
    procedure ClearData(DataRec: PDataRec);
    function CreateData(dwLoc, dwOwner: DWord; drParent: PDataRec): PDataRec;
    function ReadString(dwPos, dwLen: DWord): String;
    function GetKey(sKey: String; drParent: PDataRec): PDataRec;
  public
    constructor Create(const ARegFile: String);
    destructor Destroy; override;
    procedure DumpData(const sFile: String);
    function GetKeyList(const sParent: String): String;
    function GetValueList(const sParent: String): String;
    function ValueExists(const sKey: String): Boolean;
    function ReadValue(const sKey: String): String;
  end;

implementation

constructor TNSRegistry.Create(const ARegFile: String);
begin
  FRootData := nil;
  inherited Create;

  if not FileExists(ARegFile) then
    Exit;

  ReadRegistry(ARegFile);
end;

destructor TNSRegistry.Destroy;
begin
  ClearData(FRootData);
  inherited Destroy;
end;

procedure TNSRegistry.ReadRegistry(const ARegFile: String);
var
  dwLoc: DWord;
begin
  FStream := TFileStream.Create(ARegFile, fmOpenRead);

  try
    FStream.Seek(12, soFromBeginning);
    FStream.Read(dwLoc, SizeOf(DWord));
    FRootData := CreateData(dwLoc, 0, nil);
  finally
    FStream.Free;
  end;
end;

procedure TNSRegistry.ClearData(DataRec: PDataRec);
var
  intA: Integer;
begin
  if assigned(DataRec) then begin
    for intA := 0 to DataRec^.Children.Count - 1 do
      ClearData(PDataRec(DataRec^.Children[intA]));

    Dispose(DataRec);
  end;
end;

function TNSRegistry.CreateData(
  dwLoc, dwOwner: DWord; drParent: PDataRec): PDataRec;
var
  RegEntry: PRegEntry;
begin
  Result := nil;

  {Pointer integrity checks.}
  if (dwLoc < 13) or (dwLoc >= DWord((FStream.Size - SizeOf(TRegEntry)))) then
    Exit;

  RegEntry := New(PRegEntry);

  try
    {Load record containing data locations.}
    FStream.Seek(dwLoc, soFromBeginning);
    FStream.Read(RegEntry^, SizeOf(TRegEntry));

    with RegEntry^ do begin
      {record data integrity checks.}
      if (dwLoc <> dwLocation) or (dwOwner <> dwParent) then
        Exit;

      Result := New(PDataRec);
      Result^.Parent := drParent;
      Result^.Name := ReadString(dwNamePtr, wNameLen);
      Result^.Children := TList.Create;

      if wType = 1 then begin
        Result^.DataType := rdtKey;
        Result^.Value := '';

        if dwValuePtr <> 0 then
          CreateData(dwValuePtr, dwLocation, Result);

        if dwDown <> 0 then
          CreateData(dwDown, dwLocation, Result);
      end
      else begin
        Result^.DataType := rdtValue;
        Result^.Value := ReadString(dwValuePtr, dwValueLen);
      end;

      if dwLeft <> 0 then
        CreateData(dwLeft, dwOwner, drParent);
    end;

    if assigned(drParent) then
      drParent^.Children.Add(Result);
  finally
    Dispose(RegEntry);
  end;
end;

function TNSRegistry.ReadString(dwPos, dwLen: DWord): String;
var
  sTemp: String;
  intA: Integer;
begin
  Result := '';
  SetLength(sTemp, dwLen);
  FStream.Seek(dwPos, soFromBeginning);
  FStream.Read(PChar(sTemp)^, dwLen);

  for intA := 1 to dwLen do begin
    if sTemp[intA] <> #0 then
      Result := Result + sTemp[intA];
  end;
end;

procedure TNSRegistry.DumpData(const sFile: String);
var
  DataList: TStringlist;

  {DumpData outputs the database to a text file, indented to illustrate the
  relationships between the levels of data. This is for testing only.}

  procedure AddToList(DataRec: PDataRec; sIndent: String);
  var
    intA: Integer;
  begin
    with DataRec^ do begin
      if Value <> '' then
        DataList.Add(Format('%s%s=%s', [sIndent, Name, Value]))
      else
        DataList.Add(sIndent + Name);

      for intA := 0 to Children.Count - 1 do
        AddToList(PDataRec(Children[intA]), sIndent + '  ');

      if Children.Count > 0 then
        DataList.Add(#13#10);
    end;
  end;

begin
  DataList := TStringlist.Create;

  try
    AddToList(FRootData, '');
    DataList.SaveToFile(sFile);
  finally
    DataList.Free;
  end;
end;

function TNSRegistry.GetKeyList(const sParent: String): String;
var
  DataRec: PDataRec;
  intA: Integer;
begin
  {Returns all the keys under sParent, CRLF separated so they can be pumped
  into a Stringlist's .Text member.}
  Result := '';

  if Pos('/\', sParent) = 1 then
    DataRec := GetKey(Copy(sParent, 3, Length(sParent)), FRootData)
  else
    DataRec := GetKey(sParent, FRootData);

    if DataRec = nil then
      Exit;

  for intA := 0 to DataRec^.Children.Count - 1 do begin
    if PDataRec(DataRec^.Children[intA])^.DataType = rdtKey then begin
      if Result = '' then
        Result := PDataRec(DataRec^.Children[intA])^.Name
      else
        Result := Result + #13#10 + PDataRec(DataRec^.Children[intA])^.Name;
    end;
  end;
end;

function TNSRegistry.GetValueList(const sParent: String): String;
var
  DataRec: PDataRec;
  intA: Integer;
begin
  {Returns all the value names under sParent, CRLF separated so they can be
  pumped into a Stringlist's .Text member.}
  Result := '';

  if Pos('/\', sParent) = 1 then
    DataRec := GetKey(Copy(sParent, 3, Length(sParent)), FRootData)
  else
    DataRec := GetKey(sParent, FRootData);

  for intA := 0 to DataRec^.Children.Count - 1 do begin
    if PDataRec(DataRec^.Children[intA])^.DataType = rdtValue then begin
      if Result = '' then
        Result := PDataRec(DataRec^.Children[intA])^.Name
      else
        Result := Result + #13#10 + PDataRec(DataRec^.Children[intA])^.Name;
    end;
  end;
end;

function TNSRegistry.ValueExists(const sKey: String): Boolean;
var
  intA: Integer;
  sTemp: String;
  DataRec: PDataRec;
begin
  {Returns false if the key doesn't exist or the key has no value.}
  Result := False;
  intA := Length(sKey);

  while sKey[intA] <> '\' do
    Dec(intA);

  sTemp := Copy(sKey, intA + 1, Length(sKey));

  if Pos('/\', sKey) = 1 then
    DataRec := GetKey(Copy(sKey, 3, intA - 3), FRootData)
  else
    DataRec := GetKey(Copy(sKey, 1, intA - 1), FRootData);

  if not assigned(DataRec) then
    Exit;

  {Do a case-insensitive check just in case.}
  for intA := 0 to DataRec^.Children.Count - 1 do begin
    if PDataRec(DataRec^.Children[intA])^.DataType = rdtValue then begin
      if Lowercase(sTemp) = Lowercase(PDataRec(DataRec^.Children[intA])^.Name)
      then begin
        Result := True;
        Break;
      end;
    end;
  end;
end;

function TNSRegistry.ReadValue(const sKey: String): String;
var
  intA: Integer;
  sTemp: String;
  DataRec: PDataRec;
begin
  {Returns the value for the key if it exists, or an empty string if it
  doesn't.}
  Result := '';
  intA := Length(sKey);

  while sKey[intA] <> '\' do
    Dec(intA);

  sTemp := Copy(sKey, intA + 1, Length(sKey));

  if Pos('/\', sKey) = 1 then
    DataRec := GetKey(Copy(sKey, 3, intA - 3), FRootData)
  else
    DataRec := GetKey(Copy(sKey, 1, intA - 1), FRootData);

  if not assigned(DataRec) then
    Exit;

  {Do a case-insensitive check just in case.}
  for intA := 0 to DataRec^.Children.Count - 1 do begin
    if PDataRec(DataRec^.Children[intA])^.DataType = rdtValue then begin
      if Lowercase(sTemp) = Lowercase(PDataRec(DataRec^.Children[intA])^.Name)
      then begin
        Result := PDataRec(DataRec^.Children[intA])^.Value;
        Break;
      end;
    end;
  end;
end;

function TNSRegistry.GetKey(sKey: String; drParent: PDataRec): PDataRec;
var
  sAtom: String;
  intA: Integer;
begin
  Result := nil;
  intA := Pos('\', sKey);

  if intA = 0 then begin
    sAtom := sKey;
    sKey := '';
  end
  else begin
    sAtom := Copy(sKey, 1, intA - 1);
    sKey := Copy(sKey, intA + 1, Length(sKey));
  end;
 

  for intA := 0 to drParent^.Children.Count - 1 do begin
    if Lowercase(PDataRec(drParent^.Children[intA])^.Name) = Lowercase(sAtom)
    then begin
      if sKey = '' then
        Result := PDataRec(drParent^.Children[intA])
      else
        Result := GetKey(sKey, PDataRec(drParent^.Children[intA]));

      Break;
    end;
  end;
end;

end.

Reply via email to