Subversion Repositories userdetect2

Rev

Rev 69 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. unit UD2_Obj;
  2.  
  3. interface
  4.  
  5. {$IF CompilerVersion >= 25.0}
  6. {$LEGACYIFEND ON}
  7. {$IFEND}
  8.  
  9. uses
  10.   Windows, SysUtils, Classes, IniFiles, Contnrs, Dialogs;
  11.  
  12. const
  13.   cchBufferSize = 2048;
  14.  
  15. type
  16.   TUD2Plugin = class(TObject)
  17.   protected
  18.     FDetectedIdentifications: TObjectList{<TUD2IdentificationEntry>};
  19.   public
  20.     PluginDLL: string;
  21.     PluginGUID: TGUID;
  22.     PluginName: WideString;
  23.     PluginVendor: WideString;
  24.     PluginVersion: WideString;
  25.     IdentificationMethodName: WideString;
  26.     function PluginGUIDString: string;
  27.     property DetectedIdentifications: TObjectList{<TUD2IdentificationEntry>}
  28.       read FDetectedIdentifications;
  29.     destructor Destroy; override;
  30.     constructor Create;
  31.     procedure AddIdentification(IdStr: WideString);
  32.   end;
  33.  
  34.   TUD2IdentificationEntry = class(TObject)
  35.   private
  36.     FIdentificationString: WideString;
  37.     FPlugin: TUD2Plugin;
  38.   public
  39.     property IdentificationString: WideString read FIdentificationString;
  40.     property Plugin: TUD2Plugin read FPlugin;
  41.     function GetPrimaryIdName: WideString;
  42.     procedure GetIdNames(sl: TStrings);
  43.     constructor Create(AIdentificationString: WideString; APlugin: TUD2Plugin);
  44.   end;
  45.  
  46.   TUD2 = class(TObject)
  47.   private
  48.     FGUIDLookup: TStrings;
  49.   protected
  50.     FLoadedPlugins: TObjectList{<TUD2Plugin>};
  51.     FIniFile: TMemIniFile;
  52.     FErrors: TStrings;
  53.     FIniFileName: string;
  54.     procedure HandleDLL(dllFile: string);
  55.   public
  56.     property IniFileName: string read FIniFileName;
  57.     property Errors: TStrings read FErrors;
  58.     property LoadedPlugins: TObjectList{<TUD2Plugin>} read FLoadedPlugins;
  59.     property IniFile: TMemIniFile read FIniFile;
  60.     procedure GetCommandList(ShortTaskName: string; outSL: TStrings);
  61.     procedure HandlePluginDir(APluginDir: string);
  62.     procedure GetTaskListing(outSL: TStrings);
  63.     constructor Create(AIniFileName: string);
  64.     destructor Destroy; override;
  65.     function TaskExists(ShortTaskName: string): boolean;
  66.     function ReadMetatagString(ShortTaskName, MetatagName: string;
  67.       DefaultVal: string): string;
  68.     function ReadMetatagBool(ShortTaskName, MetatagName: string;
  69.       DefaultVal: string): boolean;
  70.     function GetTaskName(AShortTaskName: string): string;
  71.   end;
  72.  
  73. implementation
  74.  
  75. uses
  76.   UD2_PluginIntf, UD2_Utils;
  77.  
  78. function UD2_ErrorLookup(ec: UD2_STATUSCODE): string;
  79. resourcestring
  80.   LNG_STATUS_OK               = 'Operation completed sucessfully';
  81.   LNG_STATUS_BUFFER_TOO_SMALL = 'The provided buffer is too small!';
  82.   LNG_STATUS_INVALID_ARGS     = 'The function received invalid arguments!';
  83.   LNG_STATUS_INVALID          = 'Unexpected status code %s';
  84.   LNG_STATUS_NOT_LICENSED     = 'The plugin is not licensed';
  85. begin
  86.        if ec = UD2_STATUS_OK               then result := LNG_STATUS_OK
  87.   else if ec = UD2_STATUS_BUFFER_TOO_SMALL then result := LNG_STATUS_BUFFER_TOO_SMALL
  88.   else if ec = UD2_STATUS_INVALID_ARGS     then result := LNG_STATUS_INVALID_ARGS
  89.   else if ec = UD2_STATUS_NOT_LICENSED     then result := LNG_STATUS_NOT_LICENSED
  90.   else result := Format(LNG_STATUS_INVALID, ['0x'+IntToHex(ec, 8)]);
  91. end;
  92.  
  93. { TUD2Plugin }
  94.  
  95. function TUD2Plugin.PluginGUIDString: string;
  96. begin
  97.   result := UpperCase(GUIDToString(PluginGUID));
  98. end;
  99.  
  100. procedure TUD2Plugin.AddIdentification(IdStr: WideString);
  101. begin
  102.   DetectedIdentifications.Add(TUD2IdentificationEntry.Create(IdStr, Self))
  103. end;
  104.  
  105. destructor TUD2Plugin.Destroy;
  106. begin
  107.   DetectedIdentifications.Free;
  108.   inherited;
  109. end;
  110.  
  111. constructor TUD2Plugin.Create;
  112. begin
  113.   inherited Create;
  114.   FDetectedIdentifications := TObjectList{<TUD2IdentificationEntry>}.Create(true);
  115. end;
  116.  
  117. { TUD2IdentificationEntry }
  118.  
  119. function TUD2IdentificationEntry.GetPrimaryIdName: WideString;
  120. begin
  121.   result := Plugin.IdentificationMethodName+':'+IdentificationString;
  122. end;
  123.  
  124. procedure TUD2IdentificationEntry.GetIdNames(sl: TStrings);
  125. begin
  126.   sl.Add(GetPrimaryIdName);
  127.   sl.Add(UpperCase(Plugin.IdentificationMethodName)+':'+IdentificationString);
  128.   sl.Add(LowerCase(Plugin.IdentificationMethodName)+':'+IdentificationString);
  129.   sl.Add(UpperCase(Plugin.PluginGUIDString)+':'+IdentificationString);
  130.   sl.Add(LowerCase(Plugin.PluginGUIDString)+':'+IdentificationString);
  131. end;
  132.  
  133. constructor TUD2IdentificationEntry.Create(AIdentificationString: WideString;
  134.   APlugin: TUD2Plugin);
  135. begin
  136.   inherited Create;
  137.   FIdentificationString := AIdentificationString;
  138.   FPlugin := APlugin;
  139. end;
  140.  
  141. { TUD2 }
  142.  
  143. procedure TUD2.HandleDLL(dllFile: string);
  144.  
  145.   procedure ReportError(AMsg: string);
  146.   begin
  147.     // MessageDlg(AMsg, mtError, [mbOk], 0);
  148.     Errors.Add(AMsg)
  149.   end;
  150.  
  151. var
  152.   sIdentifier: array[0..cchBufferSize-1] of WideChar;
  153.   sIdentifiers: TArrayOfString;
  154.   sPluginName: array[0..cchBufferSize-1] of WideChar;
  155.   sPluginVendor: array[0..cchBufferSize-1] of WideChar;
  156.   sPluginVersion: array[0..cchBufferSize-1] of WideChar;
  157.   sIdentificationMethodName: array[0..cchBufferSize-1] of WideChar;
  158.   sPluginConfigFile: string;
  159.   iniConfig: TINIFile;
  160.   sOverrideGUID: string;
  161.   pluginID: TGUID;
  162.   sPluginID: string;
  163.   pluginInterfaceID: TGUID;
  164.   dllHandle: cardinal;
  165.   fPluginInterfaceID: TFuncPluginInterfaceID;
  166.   fPluginIdentifier: TFuncPluginIdentifier;
  167.   fPluginNameW: TFuncPluginNameW;
  168.   fPluginVendorW: TFuncPluginVendorW;
  169.   fPluginVersionW: TFuncPluginVersionW;
  170.   fIdentificationMethodNameW: TFuncIdentificationMethodNameW;
  171.   fIdentificationStringW: TFuncIdentificationStringW;
  172.   fCheckLicense: TFuncCheckLicense;
  173.   statusCode: UD2_STATUSCODE;
  174.   pl: TUD2Plugin;
  175.   i: integer;
  176.   lngID: LANGID;
  177. resourcestring
  178.   LNG_DLL_NOT_LOADED = 'Plugin DLL "%s" could not be loaded.';
  179.   LNG_METHOD_NOT_FOUND = 'Method "%s" not found in plugin "%s". The DLL is probably not a valid plugin DLL.';
  180.   LNG_INVALID_PLUGIN = 'The plugin "%s" is not a valid plugin for this program version.';
  181.   LNG_METHOD_FAILURE = 'Error "%s" at method "%s" of plugin "%s".';
  182.   LNG_PLUGINS_SAME_GUID = 'Attention: The plugin "%s" and the plugin "%s" have the same identification GUID. The latter will not be loaded.';
  183. begin
  184.   lngID := GetSystemDefaultLangID;
  185.  
  186.   dllHandle := LoadLibrary(PChar(dllFile));
  187.   if dllHandle = 0 then
  188.   begin
  189.     ReportError(Format(LNG_DLL_NOT_LOADED, [dllFile]));
  190.   end;
  191.   try
  192.     @fPluginInterfaceID := GetProcAddress(dllHandle, mnPluginInterfaceID);
  193.     if not Assigned(fPluginInterfaceID) then
  194.     begin
  195.       ReportError(Format(LNG_METHOD_NOT_FOUND, [mnPluginInterfaceID, dllFile]));
  196.       Exit;
  197.     end;
  198.     pluginInterfaceID := fPluginInterfaceID();
  199.     if not IsEqualGUID(pluginInterfaceID, GUID_USERDETECT2_IDPLUGIN_V1) then
  200.     begin
  201.       ReportError(Format(LNG_INVALID_PLUGIN, [dllFile]));
  202.       Exit;
  203.     end;
  204.  
  205.     @fIdentificationStringW := GetProcAddress(dllHandle, mnIdentificationStringW);
  206.     if not Assigned(fIdentificationStringW) then
  207.     begin
  208.       ReportError(Format(LNG_METHOD_NOT_FOUND, [mnIdentificationStringW, dllFile]));
  209.       Exit;
  210.     end;
  211.  
  212.     @fPluginNameW := GetProcAddress(dllHandle, mnPluginNameW);
  213.     if not Assigned(fPluginNameW) then
  214.     begin
  215.       ReportError(Format(LNG_METHOD_NOT_FOUND, [mnPluginNameW, dllFile]));
  216.       Exit;
  217.     end;
  218.  
  219.     @fPluginVendorW := GetProcAddress(dllHandle, mnPluginVendorW);
  220.     if not Assigned(fPluginVendorW) then
  221.     begin
  222.       ReportError(Format(LNG_METHOD_NOT_FOUND, [mnPluginVendorW, dllFile]));
  223.       Exit;
  224.     end;
  225.  
  226.     @fPluginVersionW := GetProcAddress(dllHandle, mnPluginVersionW);
  227.     if not Assigned(fPluginVersionW) then
  228.     begin
  229.       ReportError(Format(LNG_METHOD_NOT_FOUND, [mnPluginVersionW, dllFile]));
  230.       Exit;
  231.     end;
  232.  
  233.     @fCheckLicense := GetProcAddress(dllHandle, mnCheckLicense);
  234.     if not Assigned(fCheckLicense) then
  235.     begin
  236.       ReportError(Format(LNG_METHOD_NOT_FOUND, [mnCheckLicense, dllFile]));
  237.       Exit;
  238.     end;
  239.  
  240.     @fIdentificationMethodNameW := GetProcAddress(dllHandle, mnIdentificationMethodNameW);
  241.     if not Assigned(fIdentificationMethodNameW) then
  242.     begin
  243.       ReportError(Format(LNG_METHOD_NOT_FOUND, [mnIdentificationMethodNameW, dllFile]));
  244.       Exit;
  245.     end;
  246.  
  247.     sPluginID := '';
  248.  
  249.     sPluginConfigFile := ChangeFileExt(dllFile, '.ini');
  250.     if FileExists(sPluginConfigFile) then
  251.     begin
  252.       iniConfig := TIniFile.Create(sPluginConfigFile);
  253.       try
  254.         sOverrideGUID := iniConfig.ReadString('Compatibility', 'OverrideGUID', '');
  255.         if sOverrideGUID <> '' then
  256.         begin
  257.           sPluginID := sOverrideGUID;
  258.           pluginID := StringToGUID(sPluginID);
  259.         end;
  260.       finally
  261.         iniConfig.Free;
  262.       end;
  263.     end;
  264.  
  265.     if sPluginID = '' then
  266.     begin
  267.       @fPluginIdentifier := GetProcAddress(dllHandle, mnPluginIdentifier);
  268.       if not Assigned(fPluginIdentifier) then
  269.       begin
  270.         ReportError(Format(LNG_METHOD_NOT_FOUND, [mnPluginIdentifier, dllFile]));
  271.         Exit;
  272.       end;
  273.       pluginID := fPluginIdentifier();
  274.       sPluginID := GUIDToString(pluginID);
  275.     end;
  276.  
  277.     if (FGUIDLookup.Values[sPluginID] <> '') and (FGUIDLookup.Values[sPluginID] <> dllFile) then
  278.     begin
  279.       ReportError(Format(LNG_PLUGINS_SAME_GUID, [FGUIDLookup.Values[sPluginID], dllFile]));
  280.       Exit;
  281.     end
  282.     else
  283.     begin
  284.       FGUIDLookup.Values[GUIDToString(pluginID)] := dllFile;
  285.     end;
  286.  
  287.     statusCode := fCheckLicense(nil);
  288.     if statusCode <> UD2_STATUS_OK then
  289.     begin
  290.       ReportError(Format(LNG_METHOD_FAILURE, [UD2_ErrorLookup(statusCode), mnCheckLicense, dllFile]));
  291.       Exit;
  292.     end;
  293.  
  294.     statusCode := fPluginNameW(@sPluginName, cchBufferSize, lngID);
  295.     if statusCode <> UD2_STATUS_OK then
  296.     begin
  297.       ReportError(Format(LNG_METHOD_FAILURE, [UD2_ErrorLookup(statusCode), mnPluginNameW, dllFile]));
  298.       Exit;
  299.     end;
  300.  
  301.     statusCode := fPluginVendorW(@sPluginVendor, cchBufferSize, lngID);
  302.     if statusCode <> UD2_STATUS_OK then
  303.     begin
  304.       ReportError(Format(LNG_METHOD_FAILURE, [UD2_ErrorLookup(statusCode), mnPluginVendorW, dllFile]));
  305.       Exit;
  306.     end;
  307.  
  308.     statusCode := fPluginVersionW(@sPluginVersion, cchBufferSize, lngID);
  309.     if statusCode <> UD2_STATUS_OK then
  310.     begin
  311.       ReportError(Format(LNG_METHOD_FAILURE, [UD2_ErrorLookup(statusCode), mnPluginVersionW, dllFile]));
  312.       Exit;
  313.     end;
  314.  
  315.     statusCode := fIdentificationMethodNameW(@sIdentificationMethodName, cchBufferSize);
  316.     if statusCode <> UD2_STATUS_OK then
  317.     begin
  318.       ReportError(Format(LNG_METHOD_FAILURE, [UD2_ErrorLookup(statusCode), mnIdentificationMethodNameW, dllFile]));
  319.       Exit;
  320.     end;
  321.  
  322.     pl := TUD2Plugin.Create;
  323.     pl.PluginDLL     := dllFile;
  324.     pl.PluginGUID    := pluginID;
  325.     pl.PluginName    := sPluginName;
  326.     pl.PluginVendor  := sPluginVendor;
  327.     pl.PluginVersion := sPluginVersion;
  328.     pl.IdentificationMethodName := sIdentificationMethodName;
  329.     LoadedPlugins.Add(pl);
  330.  
  331.     statusCode := fIdentificationStringW(@sIdentifier, cchBufferSize);
  332.     if statusCode <> UD2_STATUS_OK then
  333.     begin
  334.       ReportError(Format(LNG_METHOD_FAILURE, [UD2_ErrorLookup(statusCode), mnIdentificationStringW, dllFile]));
  335.       Exit;
  336.     end;
  337.  
  338.     if sIdentifier = '' then Exit;
  339.  
  340.     // Multiple identifiers (e.g. multiple MAC addresses are delimited via #10 )
  341.     SetLength(sIdentifiers, 0);
  342.     sIdentifiers := SplitString(UD2_MULTIPLE_ITEMS_DELIMITER, sIdentifier);
  343.     for i := Low(sIdentifiers) to High(sIdentifiers) do
  344.     begin
  345.       pl.AddIdentification(sIdentifiers[i]);
  346.     end;
  347.   finally
  348.     FreeLibrary(dllHandle);
  349.   end;
  350. end;
  351.  
  352. procedure TUD2.HandlePluginDir(APluginDir: string);
  353. Var
  354.   SR: TSearchRec;
  355.   path: string;
  356. begin
  357.   path := IncludeTrailingPathDelimiter(APluginDir);
  358.   if FindFirst(path + '*.dll', 0, SR) = 0 then
  359.   begin
  360.     repeat
  361.       try
  362.         HandleDLL(path + sr.Name);
  363.       except
  364.         on E: Exception do
  365.         begin
  366.           MessageDlg(E.Message, mtError, [mbOK], 0);
  367.         end;
  368.       end;
  369.     until FindNext(SR) <> 0;
  370.     FindClose(SR);
  371.   end;
  372. end;
  373.  
  374. destructor TUD2.Destroy;
  375. begin
  376.   FIniFile.Free;
  377.   FLoadedPlugins.Free;
  378.   FGUIDLookup.Free;
  379.   FErrors.Free;
  380. end;
  381.  
  382. constructor TUD2.Create(AIniFileName: string);
  383. begin
  384.   FIniFileName := AIniFileName;
  385.   FLoadedPlugins := TObjectList{<TUD2Plugin>}.Create(true);
  386.   FIniFile := TMemIniFile.Create(IniFileName);
  387.   FGUIDLookup := TStringList.Create;
  388.   FErrors := TStringList.Create;
  389. end;
  390.  
  391. function TUD2.GetTaskName(AShortTaskName: string): string;
  392. begin
  393.   result := FIniFile.ReadString(AShortTaskName, 'Description', '('+AShortTaskName+')');
  394. end;
  395.  
  396. procedure TUD2.GetTaskListing(outSL: TStrings);
  397. var
  398.   sl: TStringList;
  399.   i: integer;
  400.   desc: string;
  401. begin
  402.   sl := TStringList.Create;
  403.   try
  404.     FIniFile.ReadSections(sl);
  405.     for i := 0 to sl.Count-1 do
  406.     begin
  407.       desc := GetTaskName(sl.Strings[i]);
  408.       outSL.Values[sl.Strings[i]] := desc;
  409.     end;
  410.   finally
  411.     sl.Free;
  412.   end;
  413. end;
  414.  
  415. function TUD2.TaskExists(ShortTaskName: string): boolean;
  416. begin
  417.   result := FIniFile.SectionExists(ShortTaskName);
  418. end;
  419.  
  420. function TUD2.ReadMetatagString(ShortTaskName, MetatagName: string;
  421.   DefaultVal: string): string;
  422. begin
  423.   result := IniFile.ReadString(ShortTaskName, MetatagName, DefaultVal);
  424. end;
  425.  
  426. function TUD2.ReadMetatagBool(ShortTaskName, MetatagName: string;
  427.   DefaultVal: string): boolean;
  428. begin
  429.   // DefaultVal is a string, because we want to allow an empty string, in case the
  430.   // user wishes an Exception in case the string is not a valid boolean string
  431.   result := BetterInterpreteBool(IniFile.ReadString(ShortTaskName, MetatagName, DefaultVal));
  432. end;
  433.  
  434. (*
  435.  
  436. NAMING EXAMPLE: ComputerName:ABC&&User:John=calc.exe
  437.  
  438.         idTerm:       ComputerName:ABC&&User:John
  439.         idName:       ComputerName:ABC
  440.         IdMethodName: ComputerName
  441.         IdStr         ABC
  442.         cmd:          calc.exe
  443.  
  444. *)
  445.  
  446. procedure TUD2.GetCommandList(ShortTaskName: string; outSL: TStrings);
  447. var
  448.   i, j: integer;
  449.   cmd: string;
  450.   idTerm, idName: WideString;
  451.   slSV, slIdNames: TStrings;
  452.   x: TArrayOfString;
  453.   nameVal: TArrayOfString;
  454.   FulfilsEverySubterm: boolean;
  455.   pl: TUD2Plugin;
  456.   ude: TUD2IdentificationEntry;
  457. begin
  458.   SetLength(x, 0);
  459.   SetLength(nameVal, 0);
  460.  
  461.   slIdNames := TStringList.Create;
  462.   try
  463.     for i := 0 to LoadedPlugins.Count-1 do
  464.     begin
  465.       pl := LoadedPlugins.Items[i] as TUD2Plugin;
  466.       for j := 0 to pl.DetectedIdentifications.Count-1 do
  467.       begin
  468.         ude := pl.DetectedIdentifications.Items[j] as TUD2IdentificationEntry;
  469.         ude.GetIdNames(slIdNames);
  470.       end;
  471.     end;
  472.  
  473.     slSV := TStringList.Create;
  474.     try
  475.       FIniFile.ReadSectionValues(ShortTaskName, slSV);
  476.       for j := 0 to slSV.Count-1 do
  477.       begin
  478.         // We are doing the interpretation of the line ourselves, because
  479.         // TStringList.Values[] would not allow multiple command lines with the
  480.         // same key (idTerm)
  481.         nameVal := SplitString('=', slSV.Strings[j]);
  482.         idTerm := nameVal[0];
  483.         cmd    := nameVal[1];
  484.  
  485.         if Pos(':', idTerm) = 0 then Continue;
  486.         x := SplitString('&&', idTerm);
  487.         FulfilsEverySubterm := true;
  488.         for i := Low(x) to High(x) do
  489.         begin
  490.           idName := x[i];
  491.  
  492.           if slIdNames.IndexOf(idName) = -1 then
  493.           begin
  494.             FulfilsEverySubterm := false;
  495.             break;
  496.           end;
  497.         end;
  498.  
  499.         if FulfilsEverySubterm then outSL.Add(cmd);
  500.       end;
  501.     finally
  502.       slSV.Free;
  503.     end;
  504.   finally
  505.     slIdNames.Free;
  506.   end;
  507. end;
  508.  
  509. end.
  510.