Subversion Repositories userdetect2

Rev

Rev 84 | Rev 86 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
68 daniel-mar 1
unit UD2_Obj;
2
 
3
interface
4
 
5
{$IF CompilerVersion >= 25.0}
6
{$LEGACYIFEND ON}
7
{$IFEND}
8
 
70 daniel-mar 9
{$INCLUDE 'UserDetect2.inc'}
10
 
68 daniel-mar 11
uses
71 daniel-mar 12
  Windows, SysUtils, Classes, IniFiles, Contnrs, Dialogs, UD2_PluginIntf,
85 daniel-mar 13
  UD2_PluginStatus, UD2_Utils, UD2_Parsing;
68 daniel-mar 14
 
15
type
83 daniel-mar 16
  TUD2IdentificationEntry = class;
17
 
68 daniel-mar 18
  TUD2Plugin = class(TObject)
19
  protected
20
    FDetectedIdentifications: TObjectList{<TUD2IdentificationEntry>};
21
  public
82 daniel-mar 22
    // This flag will be set if "AutoOSNotSupportedCompatibility" of the INI manifest had to be enforced/used
23
    OSNotSupportedEnforced: boolean;
24
 
68 daniel-mar 25
    PluginDLL: string;
26
    PluginGUID: TGUID;
27
    PluginName: WideString;
28
    PluginVendor: WideString;
29
    PluginVersion: WideString;
30
    IdentificationMethodName: WideString;
70 daniel-mar 31
 
32
    // ONLY contains the non-failure status code of IdentificationStringW
33
    IdentificationProcedureStatusCode: UD2_STATUS;
34
    IdentificationProcedureStatusCodeDescribed: WideString;
83 daniel-mar 35
 
69 daniel-mar 36
    Time: Cardinal;
68 daniel-mar 37
    function PluginGUIDString: string;
83 daniel-mar 38
    property DetectedIdentifications: TObjectList{<TUD2IdentificationEntry>} read FDetectedIdentifications;
68 daniel-mar 39
    destructor Destroy; override;
40
    constructor Create;
83 daniel-mar 41
    function AddIdentification(IdStr: WideString): TUD2IdentificationEntry;
42
 
43
    function InvokeDynamicCheck(dynamicData: string): boolean;
44
    function GetDynamicRequestResult(dynamicData: string): TArrayOfString;
45
 
46
    function EqualsMethodNameOrGuid(idMethodNameOrGUID: string): boolean;
68 daniel-mar 47
  end;
48
 
49
  TUD2IdentificationEntry = class(TObject)
50
  private
51
    FIdentificationString: WideString;
52
    FPlugin: TUD2Plugin;
83 daniel-mar 53
    FDynamicDataUsed: boolean;
54
    FDynamicData: string;
68 daniel-mar 55
  public
83 daniel-mar 56
    property DynamicDataUsed: boolean read FDynamicDataUsed write FDynamicDataUsed;
57
    property DynamicData: string read FDynamicData write FDynamicData;
68 daniel-mar 58
    property IdentificationString: WideString read FIdentificationString;
59
    property Plugin: TUD2Plugin read FPlugin;
60
    procedure GetIdNames(sl: TStrings);
61
    constructor Create(AIdentificationString: WideString; APlugin: TUD2Plugin);
62
  end;
63
 
64
  TUD2 = class(TObject)
65
  private
70 daniel-mar 66
    {$IFDEF CHECK_FOR_SAME_PLUGIN_GUID}
68 daniel-mar 67
    FGUIDLookup: TStrings;
70 daniel-mar 68
    {$ENDIF}
68 daniel-mar 69
  protected
70
    FLoadedPlugins: TObjectList{<TUD2Plugin>};
71
    FIniFile: TMemIniFile;
72
    FErrors: TStrings;
73
    FIniFileName: string;
74
  public
75
    property IniFileName: string read FIniFileName;
76
    property Errors: TStrings read FErrors;
77
    property LoadedPlugins: TObjectList{<TUD2Plugin>} read FLoadedPlugins;
78
    property IniFile: TMemIniFile read FIniFile;
84 daniel-mar 79
    procedure GetAllDetectedIDs(outSL: TStrings);
85 daniel-mar 80
    function FulfilsEverySubterm(conds: TUD2TDFConditionArray; slIdNames: TStrings=nil): boolean; overload;
81
    function FulfilsEverySubterm(idTerm: WideString; slIdNames: TStrings=nil): boolean; overload;
82
    function CheckTerm(idTermAndCmd: string; slIdNames: TStrings=nil): TUD2CommandArray;
83 daniel-mar 83
    function FindPluginByMethodNameOrGuid(idMethodName: string): TUD2Plugin;
85 daniel-mar 84
    function GetCommandList(ShortTaskName: string): TUD2CommandArray;
80 daniel-mar 85
    procedure HandlePluginDir(APluginDir, AFileMask: string);
68 daniel-mar 86
    procedure GetTaskListing(outSL: TStrings);
87
    constructor Create(AIniFileName: string);
88
    destructor Destroy; override;
89
    function TaskExists(ShortTaskName: string): boolean;
80 daniel-mar 90
    function ReadMetatagString(ShortTaskName, MetatagName: string; DefaultVal: string): string;
91
    function ReadMetatagBool(ShortTaskName, MetatagName: string; DefaultVal: string): boolean;
68 daniel-mar 92
    function GetTaskName(AShortTaskName: string): string;
71 daniel-mar 93
    class function GenericErrorLookup(grStatus: UD2_STATUS): string;
68 daniel-mar 94
  end;
95
 
96
implementation
97
 
98
uses
83 daniel-mar 99
  Math;
68 daniel-mar 100
 
84 daniel-mar 101
const
102
  cchBufferSize = 32768;
103
 
69 daniel-mar 104
type
105
  TUD2PluginLoader = class(TThread)
106
  protected
107
    dllFile: string;
108
    lngID: LANGID;
83 daniel-mar 109
    useDynamicData: boolean;
110
    dynamicData: WideString;
69 daniel-mar 111
    procedure Execute; override;
70 daniel-mar 112
    function HandleDLL: boolean;
69 daniel-mar 113
  public
83 daniel-mar 114
    pl: TUD2Plugin; // TODO: why do we need it?! can it be leaked if we use it for dynamic requests?
69 daniel-mar 115
    Errors: TStringList;
83 daniel-mar 116
    ResultIdentifiers: TArrayOfString;
117
    constructor Create(Suspended: boolean; DLL: string; alngid: LANGID; useDynamicData: boolean; dynamicData: WideString);
69 daniel-mar 118
    destructor Destroy; override;
119
  end;
120
 
71 daniel-mar 121
class function TUD2.GenericErrorLookup(grStatus: UD2_STATUS): string;
68 daniel-mar 122
resourcestring
80 daniel-mar 123
  LNG_STATUS_OK_UNSPECIFIED               = 'Success (Unspecified)';
124
  LNG_STATUS_OK_SINGLELINE                = 'Success (One identifier returned)';
125
  LNG_STATUS_OK_MULTILINE                 = 'Success (Multiple identifiers returned)';
126
  LNG_UNKNOWN_SUCCESS                     = 'Success (Unknown status code %s)';
69 daniel-mar 127
 
80 daniel-mar 128
  LNG_STATUS_NOTAVAIL_UNSPECIFIED         = 'Not available (Unspecified)';
129
  LNG_STATUS_NOTAVAIL_OS_NOT_SUPPORTED    = 'Not available (Operating system not supported)';
130
  LNG_STATUS_NOTAVAIL_HW_NOT_SUPPORTED    = 'Not available (Hardware not supported)';
131
  LNG_STATUS_NOTAVAIL_NO_ENTITIES         = 'Not available (No entities to identify)';
132
  LNG_STATUS_NOTAVAIL_WINAPI_CALL_FAILURE = 'Not available (A Windows API call failed. Message: %s)';
83 daniel-mar 133
  LNG_STATUS_NOTAVAIL_ONLY_ACCEPT_DYNAMIC = 'Not available (Arguments required)';
80 daniel-mar 134
  LNG_UNKNOWN_NOTAVAIL                    = 'Not available (Unknown status code %s)';
69 daniel-mar 135
 
82 daniel-mar 136
  LNG_STATUS_FAILURE_UNSPECIFIED          = 'Error (Unspecified)';
137
  LNG_STATUS_FAILURE_BUFFER_TOO_SMALL     = 'Error (The provided buffer is too small!)';
138
  LNG_STATUS_FAILURE_INVALID_ARGS         = 'Error (The function received invalid arguments!)';
139
  LNG_STATUS_FAILURE_PLUGIN_NOT_LICENSED  = 'Error (The plugin is not licensed)';
140
  LNG_STATUS_FAILURE_NO_RETURNED_VALUE    = 'Error (Plugin did not return a status)';
141
  LNG_STATUS_FAILURE_CATCHED_EXCEPTION    = 'Error (Catched unexpected Exception)';
80 daniel-mar 142
  LNG_UNKNOWN_FAILED                      = 'Error (Unknown status code %s)';
69 daniel-mar 143
 
71 daniel-mar 144
  LNG_UNKNOWN_STATUS                      = 'Unknown status code with unexpected category: %s';
68 daniel-mar 145
begin
71 daniel-mar 146
       if UD2_STATUS_Equal(grStatus, UD2_STATUS_OK_UNSPECIFIED, false)               then result := LNG_STATUS_OK_UNSPECIFIED
147
  else if UD2_STATUS_Equal(grStatus, UD2_STATUS_OK_SINGLELINE, false)                then result := LNG_STATUS_OK_SINGLELINE
148
  else if UD2_STATUS_Equal(grStatus, UD2_STATUS_OK_MULTILINE, false)                 then result := LNG_STATUS_OK_MULTILINE
69 daniel-mar 149
 
71 daniel-mar 150
  else if UD2_STATUS_Equal(grStatus, UD2_STATUS_NOTAVAIL_UNSPECIFIED, false)         then result := LNG_STATUS_NOTAVAIL_UNSPECIFIED
151
  else if UD2_STATUS_Equal(grStatus, UD2_STATUS_NOTAVAIL_OS_NOT_SUPPORTED, false)    then result := LNG_STATUS_NOTAVAIL_OS_NOT_SUPPORTED
152
  else if UD2_STATUS_Equal(grStatus, UD2_STATUS_NOTAVAIL_HW_NOT_SUPPORTED, false)    then result := LNG_STATUS_NOTAVAIL_HW_NOT_SUPPORTED
153
  else if UD2_STATUS_Equal(grStatus, UD2_STATUS_NOTAVAIL_NO_ENTITIES, false)         then result := LNG_STATUS_NOTAVAIL_NO_ENTITIES
154
  else if UD2_STATUS_Equal(grStatus, UD2_STATUS_NOTAVAIL_WINAPI_CALL_FAILURE, false) then result := Format(LNG_STATUS_NOTAVAIL_WINAPI_CALL_FAILURE, [FormatOSError(grStatus.dwExtraInfo)])
83 daniel-mar 155
  else if UD2_STATUS_Equal(grStatus, UD2_STATUS_NOTAVAIL_ONLY_ACCEPT_DYNAMIC, false) then result := LNG_STATUS_NOTAVAIL_ONLY_ACCEPT_DYNAMIC
69 daniel-mar 156
 
82 daniel-mar 157
  else if UD2_STATUS_Equal(grStatus, UD2_STATUS_FAILURE_UNSPECIFIED, false)          then result := LNG_STATUS_FAILURE_UNSPECIFIED
158
  else if UD2_STATUS_Equal(grStatus, UD2_STATUS_FAILURE_BUFFER_TOO_SMALL, false)     then result := LNG_STATUS_FAILURE_BUFFER_TOO_SMALL
159
  else if UD2_STATUS_Equal(grStatus, UD2_STATUS_FAILURE_INVALID_ARGS, false)         then result := LNG_STATUS_FAILURE_INVALID_ARGS
160
  else if UD2_STATUS_Equal(grStatus, UD2_STATUS_FAILURE_PLUGIN_NOT_LICENSED, false)  then result := LNG_STATUS_FAILURE_PLUGIN_NOT_LICENSED
161
  else if UD2_STATUS_Equal(grStatus, UD2_STATUS_FAILURE_NO_RETURNED_VALUE, false)    then result := LNG_STATUS_FAILURE_NO_RETURNED_VALUE
162
  else if UD2_STATUS_Equal(grStatus, UD2_STATUS_FAILURE_CATCHED_EXCEPTION, false)    then result := LNG_STATUS_FAILURE_CATCHED_EXCEPTION
69 daniel-mar 163
 
71 daniel-mar 164
  else if grStatus.wCategory = UD2_STATUSCAT_SUCCESS   then result := Format(LNG_UNKNOWN_SUCCESS,  [UD2_STATUS_FormatStatusCode(grStatus)])
165
  else if grStatus.wCategory = UD2_STATUSCAT_NOT_AVAIL then result := Format(LNG_UNKNOWN_NOTAVAIL, [UD2_STATUS_FormatStatusCode(grStatus)])
166
  else if grStatus.wCategory = UD2_STATUSCAT_FAILED    then result := Format(LNG_UNKNOWN_FAILED,   [UD2_STATUS_FormatStatusCode(grStatus)])
167
  else                                                      result := Format(LNG_UNKNOWN_STATUS,   [UD2_STATUS_FormatStatusCode(grStatus)]);
68 daniel-mar 168
end;
169
 
170
{ TUD2Plugin }
171
 
172
function TUD2Plugin.PluginGUIDString: string;
173
begin
174
  result := UpperCase(GUIDToString(PluginGUID));
175
end;
176
 
83 daniel-mar 177
function TUD2Plugin.AddIdentification(IdStr: WideString): TUD2IdentificationEntry;
68 daniel-mar 178
begin
83 daniel-mar 179
  result := TUD2IdentificationEntry.Create(IdStr, Self);
180
  DetectedIdentifications.Add(result);
68 daniel-mar 181
end;
182
 
183
destructor TUD2Plugin.Destroy;
184
begin
185
  DetectedIdentifications.Free;
186
  inherited;
187
end;
188
 
189
constructor TUD2Plugin.Create;
190
begin
191
  inherited Create;
192
  FDetectedIdentifications := TObjectList{<TUD2IdentificationEntry>}.Create(true);
193
end;
194
 
83 daniel-mar 195
function TUD2Plugin.InvokeDynamicCheck(dynamicData: string): boolean;
196
var
197
  ude: TUD2IdentificationEntry;
198
  i: integer;
199
  ids: TArrayOfString;
200
  id: string;
201
begin
202
  result := false;
68 daniel-mar 203
 
83 daniel-mar 204
  for i := 0 to FDetectedIdentifications.Count-1 do
205
  begin
206
    ude := FDetectedIdentifications.Items[i] as TUD2IdentificationEntry;
207
    if ude.dynamicDataUsed and (ude.dynamicData = dynamicData) then
208
    begin
209
      // The dynamic content was already evaluated (and therefore is already added in FDetectedIdentifications).
210
      Exit;
211
    end;
212
  end;
213
 
214
  SetLength(ids, 0);
215
  ids := GetDynamicRequestResult(dynamicData);
216
 
217
  for i := 0 to Length(ids)-1 do
218
  begin
219
    id := ids[i];
220
 
221
    ude := AddIdentification(id);
222
    ude.dynamicDataUsed := true;
223
    ude.dynamicData := dynamicData;
224
 
225
    result := true;
226
  end;
227
end;
228
 
229
function TUD2Plugin.GetDynamicRequestResult(dynamicData: string): TArrayOfString;
230
var
231
  lngID: LANGID;
232
  pll: TUD2PluginLoader;
68 daniel-mar 233
begin
83 daniel-mar 234
  lngID := GetSystemDefaultLangID;
235
 
236
  pll := TUD2PluginLoader.Create(false, PluginDLL, lngid, true, dynamicData);
237
  try
238
    pll.WaitFor;
239
    result := pll.ResultIdentifiers;
240
  finally
241
    pll.Free;
242
  end;
68 daniel-mar 243
end;
244
 
83 daniel-mar 245
function TUD2Plugin.EqualsMethodNameOrGuid(idMethodNameOrGUID: string): boolean;
246
begin
247
  result := SameText(IdentificationMethodName, idMethodNameOrGUID) or
248
            SameText(GUIDToString(PluginGUID), idMethodNameOrGUID)
249
end;
250
 
251
{ TUD2IdentificationEntry }
252
 
68 daniel-mar 253
procedure TUD2IdentificationEntry.GetIdNames(sl: TStrings);
85 daniel-mar 254
var
255
  cond: TUD2TDFCondition;
68 daniel-mar 256
begin
85 daniel-mar 257
  cond.idMethodName := Plugin.IdentificationMethodName;
258
  cond.idStr := IdentificationString;
259
  cond.dynamicDataUsed := DynamicDataUsed;
260
  cond.dynamicData := DynamicData;
261
  sl.Add(UD2_CondToStr(cond));
262
 
263
  cond.idMethodName := Plugin.PluginGUIDString;
264
  sl.Add(UD2_CondToStr(cond));
68 daniel-mar 265
end;
266
 
267
constructor TUD2IdentificationEntry.Create(AIdentificationString: WideString;
268
  APlugin: TUD2Plugin);
269
begin
270
  inherited Create;
271
  FIdentificationString := AIdentificationString;
272
  FPlugin := APlugin;
273
end;
274
 
275
{ TUD2 }
276
 
80 daniel-mar 277
procedure TUD2.HandlePluginDir(APluginDir, AFileMask: string);
69 daniel-mar 278
Var
279
  SR: TSearchRec;
280
  path: string;
70 daniel-mar 281
  pluginLoader: TUD2PluginLoader;
69 daniel-mar 282
  tob: TObjectList;
68 daniel-mar 283
  i: integer;
70 daniel-mar 284
  {$IFDEF CHECK_FOR_SAME_PLUGIN_GUID}
285
  sPluginID, prevDLL: string;
286
  {$ENDIF}
69 daniel-mar 287
  lngid: LANGID;
68 daniel-mar 288
resourcestring
289
  LNG_PLUGINS_SAME_GUID = 'Attention: The plugin "%s" and the plugin "%s" have the same identification GUID. The latter will not be loaded.';
290
begin
69 daniel-mar 291
  tob := TObjectList.Create;
68 daniel-mar 292
  try
69 daniel-mar 293
    tob.OwnsObjects := false;
68 daniel-mar 294
 
69 daniel-mar 295
    lngID := GetSystemDefaultLangID;
68 daniel-mar 296
 
80 daniel-mar 297
    path := APluginDir;
298
    if path <> '' then path := IncludeTrailingPathDelimiter(path);
299
 
300
    if FindFirst(path + AFileMask, 0, SR) = 0 then
68 daniel-mar 301
    begin
302
      try
69 daniel-mar 303
        repeat
304
          try
83 daniel-mar 305
            tob.Add(TUD2PluginLoader.Create(false, path + sr.Name, lngid, false, ''));
69 daniel-mar 306
          except
307
            on E: Exception do
308
            begin
309
              MessageDlg(E.Message, mtError, [mbOK], 0);
310
            end;
311
          end;
312
        until FindNext(SR) <> 0;
68 daniel-mar 313
      finally
69 daniel-mar 314
        FindClose(SR);
68 daniel-mar 315
      end;
316
    end;
317
 
69 daniel-mar 318
    for i := 0 to tob.count-1 do
68 daniel-mar 319
    begin
70 daniel-mar 320
      pluginLoader := tob.items[i] as TUD2PluginLoader;
321
      pluginLoader.WaitFor;
322
      Errors.AddStrings(pluginLoader.Errors);
323
      {$IFDEF CHECK_FOR_SAME_PLUGIN_GUID}
324
      if Assigned(pluginLoader.pl) then
68 daniel-mar 325
      begin
82 daniel-mar 326
        if not pluginLoader.pl.OSNotSupportedEnforced then
69 daniel-mar 327
        begin
82 daniel-mar 328
          sPluginID := GUIDToString(pluginLoader.pl.PluginGUID);
329
          prevDLL := FGUIDLookup.Values[sPluginID];
330
          if (prevDLL <> '') and (prevDLL <> pluginLoader.pl.PluginDLL) then
331
          begin
332
            Errors.Add(Format(LNG_PLUGINS_SAME_GUID, [prevDLL, pluginLoader.pl.PluginDLL]));
333
            pluginLoader.pl.Free;
334
          end
335
          else
336
          begin
337
            FGUIDLookup.Values[sPluginID] := pluginLoader.pl.PluginDLL;
338
            LoadedPlugins.Add(pluginLoader.pl);
339
          end;
69 daniel-mar 340
        end;
68 daniel-mar 341
      end;
70 daniel-mar 342
      {$ENDIF}
343
      pluginLoader.Free;
68 daniel-mar 344
    end;
345
  finally
69 daniel-mar 346
    tob.free;
68 daniel-mar 347
  end;
348
end;
349
 
350
destructor TUD2.Destroy;
351
begin
352
  FIniFile.Free;
353
  FLoadedPlugins.Free;
70 daniel-mar 354
  {$IFDEF CHECK_FOR_SAME_PLUGIN_GUID}
68 daniel-mar 355
  FGUIDLookup.Free;
70 daniel-mar 356
  {$ENDIF}
68 daniel-mar 357
  FErrors.Free;
358
end;
359
 
360
constructor TUD2.Create(AIniFileName: string);
361
begin
362
  FIniFileName := AIniFileName;
363
  FLoadedPlugins := TObjectList{<TUD2Plugin>}.Create(true);
364
  FIniFile := TMemIniFile.Create(IniFileName);
70 daniel-mar 365
  {$IFDEF CHECK_FOR_SAME_PLUGIN_GUID}
68 daniel-mar 366
  FGUIDLookup := TStringList.Create;
70 daniel-mar 367
  {$ENDIF}
68 daniel-mar 368
  FErrors := TStringList.Create;
369
end;
370
 
371
function TUD2.GetTaskName(AShortTaskName: string): string;
69 daniel-mar 372
resourcestring
373
  LNG_NO_DESCRIPTION = '(%s)';
68 daniel-mar 374
begin
69 daniel-mar 375
  result := FIniFile.ReadString(AShortTaskName, 'Description', Format(LNG_NO_DESCRIPTION, [AShortTaskName]));
68 daniel-mar 376
end;
377
 
378
procedure TUD2.GetTaskListing(outSL: TStrings);
379
var
380
  sl: TStringList;
381
  i: integer;
382
  desc: string;
383
begin
384
  sl := TStringList.Create;
385
  try
386
    FIniFile.ReadSections(sl);
387
    for i := 0 to sl.Count-1 do
388
    begin
389
      desc := GetTaskName(sl.Strings[i]);
390
      outSL.Values[sl.Strings[i]] := desc;
391
    end;
392
  finally
393
    sl.Free;
394
  end;
395
end;
396
 
397
function TUD2.TaskExists(ShortTaskName: string): boolean;
398
begin
399
  result := FIniFile.SectionExists(ShortTaskName);
400
end;
401
 
402
function TUD2.ReadMetatagString(ShortTaskName, MetatagName: string;
403
  DefaultVal: string): string;
404
begin
405
  result := IniFile.ReadString(ShortTaskName, MetatagName, DefaultVal);
406
end;
407
 
408
function TUD2.ReadMetatagBool(ShortTaskName, MetatagName: string;
409
  DefaultVal: string): boolean;
410
begin
411
  // DefaultVal is a string, because we want to allow an empty string, in case the
412
  // user wishes an Exception in case the string is not a valid boolean string
413
  result := BetterInterpreteBool(IniFile.ReadString(ShortTaskName, MetatagName, DefaultVal));
414
end;
415
 
416
(*
417
 
84 daniel-mar 418
NAMING EXAMPLE: $CASESENSITIVE$ComputerName(dynXYZ):ABC&&User:John=calc.exe$RIOD$
68 daniel-mar 419
 
84 daniel-mar 420
        idTerm:       ComputerName(dynXYZ):ABC&&User:John
68 daniel-mar 421
        idName:       ComputerName:ABC
422
        IdMethodName: ComputerName
423
        IdStr         ABC
424
        cmd:          calc.exe
83 daniel-mar 425
        dynamicData:  dynXYZ
68 daniel-mar 426
 
427
*)
428
 
84 daniel-mar 429
procedure TUD2.GetAllDetectedIDs(outSL: TStrings);
68 daniel-mar 430
var
431
  i, j: integer;
432
  pl: TUD2Plugin;
433
  ude: TUD2IdentificationEntry;
434
begin
81 daniel-mar 435
  for i := 0 to LoadedPlugins.Count-1 do
436
  begin
437
    pl := LoadedPlugins.Items[i] as TUD2Plugin;
438
    for j := 0 to pl.DetectedIdentifications.Count-1 do
439
    begin
440
      ude := pl.DetectedIdentifications.Items[j] as TUD2IdentificationEntry;
441
      ude.GetIdNames(outSL);
442
    end;
443
  end;
444
end;
68 daniel-mar 445
 
85 daniel-mar 446
function TUD2.FulfilsEverySubterm(conds: TUD2TDFConditionArray; slIdNames: TStrings=nil): boolean;
447
begin
448
  result := FulfilsEverySubterm(UD2_CondsToStr(conds), slIdNames);
449
end;
450
 
81 daniel-mar 451
function TUD2.FulfilsEverySubterm(idTerm: WideString; slIdNames: TStrings=nil): boolean;
452
var
453
  i: integer;
83 daniel-mar 454
  p: TUD2Plugin;
81 daniel-mar 455
  cleanUpStringList: boolean;
85 daniel-mar 456
  conds: TUD2TDFConditionArray;
457
  cond: TUD2TDFCondition;
458
  idName: string;
81 daniel-mar 459
begin
460
  cleanUpStringList := slIdNames = nil;
68 daniel-mar 461
  try
81 daniel-mar 462
    if cleanUpStringList then
68 daniel-mar 463
    begin
81 daniel-mar 464
      slIdNames := TStringList.Create;
84 daniel-mar 465
      GetAllDetectedIDs(slIdNames);
81 daniel-mar 466
    end;
467
 
85 daniel-mar 468
    conds := UD2P_ParseConditions(idTerm);
469
 
81 daniel-mar 470
    result := true;
85 daniel-mar 471
    for i := Low(conds) to High(conds) do
81 daniel-mar 472
    begin
85 daniel-mar 473
      cond := conds[i];
81 daniel-mar 474
 
85 daniel-mar 475
      if cond.dynamicDataUsed then
83 daniel-mar 476
      begin
85 daniel-mar 477
        p := FindPluginByMethodNameOrGuid(cond.idMethodName);
478
        if Assigned(p) then
84 daniel-mar 479
        begin
85 daniel-mar 480
          if p.InvokeDynamicCheck(cond.dynamicData) then
83 daniel-mar 481
          begin
85 daniel-mar 482
            // Reload the identifications
483
            slIdNames.Clear;
484
            GetAllDetectedIDs(slIdNames);
83 daniel-mar 485
          end;
486
        end;
487
      end;
488
 
85 daniel-mar 489
      idName := UD2_CondToStr(cond);
83 daniel-mar 490
 
85 daniel-mar 491
      if (not cond.caseSensitive and (slIdNames.IndexOf(idName) = -1)) or
492
         (cond.caseSensitive and (IndexOf_CS(slIdNames, idName) = -1)) then
68 daniel-mar 493
      begin
81 daniel-mar 494
        result := false;
495
        break;
68 daniel-mar 496
      end;
497
    end;
81 daniel-mar 498
  finally
499
    if cleanUpStringList and Assigned(slIdNames) then
500
      slIdNames.Free;
501
  end;
502
end;
68 daniel-mar 503
 
83 daniel-mar 504
function TUD2.FindPluginByMethodNameOrGuid(idMethodName: string): TUD2Plugin;
505
var
506
  i: integer;
507
  p: TUD2Plugin;
508
begin
509
  result := nil;
510
  for i := 0 to LoadedPlugins.Count-1 do
511
  begin
512
    p := LoadedPlugins.Items[i] as TUD2Plugin;
513
 
514
    if p.EqualsMethodNameOrGuid(idMethodName) then
515
    begin
516
      result := p;
517
      Exit;
518
    end;
519
  end;
520
end;
521
 
85 daniel-mar 522
function TUD2.GetCommandList(ShortTaskName: string): TUD2CommandArray;
81 daniel-mar 523
var
85 daniel-mar 524
  i, j, l: integer;
81 daniel-mar 525
  slSV, slIdNames: TStrings;
85 daniel-mar 526
  tmpCmds: TUD2CommandArray;
81 daniel-mar 527
begin
85 daniel-mar 528
  SetLength(result, 0);
529
  SetLength(tmpCmds, 0);
530
 
81 daniel-mar 531
  slIdNames := TStringList.Create;
532
  try
84 daniel-mar 533
    GetAllDetectedIDs(slIdNames);
81 daniel-mar 534
 
68 daniel-mar 535
    slSV := TStringList.Create;
536
    try
537
      FIniFile.ReadSectionValues(ShortTaskName, slSV);
81 daniel-mar 538
      for i := 0 to slSV.Count-1 do
68 daniel-mar 539
      begin
85 daniel-mar 540
        tmpCmds := CheckTerm(slSV.Strings[i], slIdNames);
541
        for j := Low(tmpCmds) to High(tmpCmds) do
542
        begin
543
          l := Length(result);
544
          SetLength(result, l+1);
545
          result[l] := tmpCmds[j];
546
        end;
68 daniel-mar 547
      end;
548
    finally
549
      slSV.Free;
550
    end;
551
  finally
552
    slIdNames.Free;
553
  end;
554
end;
555
 
85 daniel-mar 556
function TUD2.CheckTerm(idTermAndCmd: string; slIdNames: TStrings=nil): TUD2CommandArray;
83 daniel-mar 557
var
558
  slIdNamesCreated: boolean;
85 daniel-mar 559
  ent: TUD2TDFEntry;
83 daniel-mar 560
begin
85 daniel-mar 561
  SetLength(result, 0);
562
 
83 daniel-mar 563
  slIdNamesCreated := false;
564
  try
565
    if not Assigned(slIdNames) then
566
    begin
567
      slIdNamesCreated := true;
568
      slIdNames := TStringList.Create;
84 daniel-mar 569
      GetAllDetectedIDs(slIdNames);
83 daniel-mar 570
    end;
571
 
85 daniel-mar 572
    if not UD2P_ParseTdfLine(idTermAndCmd, ent) then Exit;
573
    if FulfilsEverySubterm(ent.ids, slIdNames) then
574
    begin
575
      result := ent.commands;
576
    end;
83 daniel-mar 577
  finally
578
    if slIdNamesCreated then slIdNames.Free;
579
  end;
580
end;
581
 
69 daniel-mar 582
{ TUD2PluginLoader }
583
 
584
procedure TUD2PluginLoader.Execute;
585
begin
586
  inherited;
587
 
588
  HandleDLL;
589
end;
590
 
83 daniel-mar 591
constructor TUD2PluginLoader.Create(Suspended: boolean; DLL: string; alngid: LANGID; useDynamicData: boolean; dynamicData: WideString);
69 daniel-mar 592
begin
593
  inherited Create(Suspended);
594
  dllfile := dll;
595
  pl := nil;
596
  Errors := TStringList.Create;
597
  lngid := alngid;
83 daniel-mar 598
  self.useDynamicData := useDynamicData;
599
  Self.dynamicData := dynamicData;
69 daniel-mar 600
end;
601
 
602
destructor TUD2PluginLoader.Destroy;
603
begin
604
  Errors.Free;
605
  inherited;
606
end;
607
 
70 daniel-mar 608
function TUD2PluginLoader.HandleDLL: boolean;
69 daniel-mar 609
var
610
  sIdentifier: WideString;
611
  buf: array[0..cchBufferSize-1] of WideChar;
612
  pluginInterfaceID: TGUID;
70 daniel-mar 613
  dllHandle: Cardinal;
69 daniel-mar 614
  fPluginInterfaceID: TFuncPluginInterfaceID;
615
  fPluginIdentifier: TFuncPluginIdentifier;
616
  fPluginNameW: TFuncPluginNameW;
617
  fPluginVendorW: TFuncPluginVendorW;
618
  fPluginVersionW: TFuncPluginVersionW;
619
  fIdentificationMethodNameW: TFuncIdentificationMethodNameW;
620
  fIdentificationStringW: TFuncIdentificationStringW;
83 daniel-mar 621
  fDynamicIdentificationStringW: TFuncDynamicIdentificationStringW;
69 daniel-mar 622
  fCheckLicense: TFuncCheckLicense;
70 daniel-mar 623
  fDescribeOwnStatusCodeW: TFuncDescribeOwnStatusCodeW;
69 daniel-mar 624
  statusCode: UD2_STATUS;
625
  i: integer;
626
  starttime, endtime, time: cardinal;
82 daniel-mar 627
  bakErrorMode: DWORD;
628
  err: DWORD;
70 daniel-mar 629
 
630
  function _ErrorLookup(statusCode: UD2_STATUS): WideString;
631
  var
632
    ret: BOOL;
83 daniel-mar 633
    buf: array[0..cchBufferSize-1] of WideChar;
70 daniel-mar 634
  begin
82 daniel-mar 635
    if Assigned(fDescribeOwnStatusCodeW) then
70 daniel-mar 636
    begin
82 daniel-mar 637
      ZeroMemory(@buf, cchBufferSize);
638
      ret := fDescribeOwnStatusCodeW(@buf, cchBufferSize, statusCode, lngID);
639
      if ret then
640
      begin
641
        result := PWideChar(@buf);
642
        Exit;
643
      end;
70 daniel-mar 644
    end;
645
    result := TUD2.GenericErrorLookup(statusCode);
646
  end;
647
 
82 daniel-mar 648
  function _ApplyCompatibilityGUID: boolean;
649
  var
650
    iniConfig: TIniFile;
651
    sOverrideGUID: string;
652
    sPluginConfigFile: string;
69 daniel-mar 653
  begin
82 daniel-mar 654
    result := false;
69 daniel-mar 655
    sPluginConfigFile := ChangeFileExt(dllFile, '.ini');
656
    if FileExists(sPluginConfigFile) then
657
    begin
658
      iniConfig := TIniFile.Create(sPluginConfigFile);
659
      try
660
        sOverrideGUID := iniConfig.ReadString('Compatibility', 'OverrideGUID', '');
661
        if sOverrideGUID <> '' then
662
        begin
663
          pl.PluginGUID := StringToGUID(sOverrideGUID);
82 daniel-mar 664
          result := true;
69 daniel-mar 665
        end;
666
      finally
667
        iniConfig.Free;
668
      end;
669
    end;
82 daniel-mar 670
  end;
69 daniel-mar 671
 
82 daniel-mar 672
  function _AutoOSNotSupportedMode: integer;
673
  var
674
    iniConfig: TIniFile;
675
    sPluginConfigFile: string;
676
  begin
677
    result := 0;
678
    sPluginConfigFile := ChangeFileExt(dllFile, '.ini');
679
    if FileExists(sPluginConfigFile) then
69 daniel-mar 680
    begin
82 daniel-mar 681
      iniConfig := TIniFile.Create(sPluginConfigFile);
682
      try
683
        result := iniConfig.ReadInteger('Compatibility', 'AutoOSNotSupported', 0);
684
      finally
685
        iniConfig.Free;
69 daniel-mar 686
      end;
687
    end;
82 daniel-mar 688
  end;
69 daniel-mar 689
 
82 daniel-mar 690
  procedure _OverwriteStatusToOSNotSupported;
691
  begin
692
    pl := TUD2Plugin.Create;
693
    pl.PluginDLL := dllFile;
694
    statusCode := UD2_STATUS_NOTAVAIL_OS_NOT_SUPPORTED;
695
    pl.IdentificationProcedureStatusCode := statusCode;
696
    pl.IdentificationProcedureStatusCodeDescribed := _ErrorLookup(statusCode);
697
    (*
698
    if not _ApplyCompatibilityGUID then
69 daniel-mar 699
    begin
82 daniel-mar 700
      CreateGUID(pl.PluginGUID); // to avoid the "double GUID" error
69 daniel-mar 701
    end;
82 daniel-mar 702
    *)
703
    pl.OSNotSupportedEnforced := true; // to avoid the "double GUID" error
704
    result := true;
705
  end;
69 daniel-mar 706
 
82 daniel-mar 707
resourcestring
708
  LNG_DLL_NOT_LOADED = 'Plugin DLL "%s" could not be loaded: %s';
709
  LNG_METHOD_NOT_FOUND = 'Method "%s" not found in plugin "%s". The DLL is probably not a valid plugin DLL.';
710
  LNG_INVALID_PLUGIN = 'The plugin "%s" is not a valid plugin for this application.';
711
  LNG_METHOD_FAILURE = 'Error "%s" at method "%s" of plugin "%s".';
712
  LNG_EXCEPTION = 'Fatal error while loading "%s" (%s: %s)';
713
begin
714
  result := false;
715
  startTime := GetTickCount;
69 daniel-mar 716
 
82 daniel-mar 717
  try
718
    bakErrorMode := 0;
719
    UD2_SetThreadErrorMode(SEM_FAILCRITICALERRORS, Pointer(bakErrorMode));
720
    try
721
      dllHandle := LoadLibrary(PChar(dllFile));
722
      if dllHandle = 0 then
723
      begin
724
        err := GetLastError;
69 daniel-mar 725
 
82 daniel-mar 726
        if ((_AutoOSNotSupportedMode = 1) and ((err = ERROR_DLL_NOT_FOUND) or (err = ERROR_PROC_NOT_FOUND))) or
727
           (_AutoOSNotSupportedMode >= 2) then
728
        begin
729
          _OverwriteStatusToOSNotSupported;
730
          Exit;
731
        end;
69 daniel-mar 732
 
82 daniel-mar 733
        Errors.Add(Format(LNG_DLL_NOT_LOADED, [dllFile, SysErrorMessage(err)]));
734
        Exit;
735
      end;
736
      try
737
        @fPluginInterfaceID := GetProcAddress(dllHandle, mnPluginInterfaceID);
738
        if not Assigned(fPluginInterfaceID) then
739
        begin
740
          Errors.Add(Format(LNG_METHOD_NOT_FOUND, [mnPluginInterfaceID, dllFile]));
741
          Exit;
742
        end;
743
        pluginInterfaceID := fPluginInterfaceID();
744
        if not IsEqualGUID(pluginInterfaceID, GUID_USERDETECT2_IDPLUGIN_V1) then
745
        begin
746
          Errors.Add(Format(LNG_INVALID_PLUGIN, [dllFile]));
747
          Exit;
748
        end;
69 daniel-mar 749
 
83 daniel-mar 750
        fDynamicIdentificationStringW := nil;
751
        fIdentificationStringW := nil;
752
        if useDynamicData then
69 daniel-mar 753
        begin
83 daniel-mar 754
          @fDynamicIdentificationStringW := GetProcAddress(dllHandle, mnDynamicIdentificationStringW);
755
          if not Assigned(fDynamicIdentificationStringW) then
756
          begin
757
            // TODO xxx: Darf hier ein fataler Fehler entstehen, obwohl dieses Szenario nur durch die INI file auftreten kann?
758
            // TODO (allgemein): In der Modulübersicht soll auch gezeigt werden, ob dieses Modul dynamischen Content erlaubt.
759
            // TODO (allgemein): doku
760
            Errors.Add(Format(LNG_METHOD_NOT_FOUND, [mnDynamicIdentificationStringW, dllFile]));
761
            Exit;
762
          end;
763
        end
764
        else
765
        begin
766
          @fIdentificationStringW := GetProcAddress(dllHandle, mnIdentificationStringW);
767
          if not Assigned(fIdentificationStringW) then
768
          begin
769
            Errors.Add(Format(LNG_METHOD_NOT_FOUND, [mnIdentificationStringW, dllFile]));
770
            Exit;
771
          end;
69 daniel-mar 772
        end;
82 daniel-mar 773
 
774
        @fPluginNameW := GetProcAddress(dllHandle, mnPluginNameW);
775
        if not Assigned(fPluginNameW) then
776
        begin
777
          Errors.Add(Format(LNG_METHOD_NOT_FOUND, [mnPluginNameW, dllFile]));
778
          Exit;
779
        end;
780
 
781
        @fPluginVendorW := GetProcAddress(dllHandle, mnPluginVendorW);
782
        if not Assigned(fPluginVendorW) then
783
        begin
784
          Errors.Add(Format(LNG_METHOD_NOT_FOUND, [mnPluginVendorW, dllFile]));
785
          Exit;
786
        end;
787
 
788
        @fPluginVersionW := GetProcAddress(dllHandle, mnPluginVersionW);
789
        if not Assigned(fPluginVersionW) then
790
        begin
791
          Errors.Add(Format(LNG_METHOD_NOT_FOUND, [mnPluginVersionW, dllFile]));
792
          Exit;
793
        end;
794
 
795
        @fCheckLicense := GetProcAddress(dllHandle, mnCheckLicense);
796
        if not Assigned(fCheckLicense) then
797
        begin
798
          Errors.Add(Format(LNG_METHOD_NOT_FOUND, [mnCheckLicense, dllFile]));
799
          Exit;
800
        end;
801
 
802
        @fIdentificationMethodNameW := GetProcAddress(dllHandle, mnIdentificationMethodNameW);
803
        if not Assigned(fIdentificationMethodNameW) then
804
        begin
805
          Errors.Add(Format(LNG_METHOD_NOT_FOUND, [mnIdentificationMethodNameW, dllFile]));
806
          Exit;
807
        end;
808
 
809
        @fDescribeOwnStatusCodeW := GetProcAddress(dllHandle, mnDescribeOwnStatusCodeW);
810
        if not Assigned(fDescribeOwnStatusCodeW) then
811
        begin
812
          Errors.Add(Format(LNG_METHOD_NOT_FOUND, [mnDescribeOwnStatusCodeW, dllFile]));
813
          Exit;
814
        end;
815
 
816
        pl := TUD2Plugin.Create;
817
        pl.PluginDLL := dllFile;
818
 
819
        if not _ApplyCompatibilityGUID then
820
        begin
821
          @fPluginIdentifier := GetProcAddress(dllHandle, mnPluginIdentifier);
822
          if not Assigned(fPluginIdentifier) then
823
          begin
824
            Errors.Add(Format(LNG_METHOD_NOT_FOUND, [mnPluginIdentifier, dllFile]));
825
            Exit;
826
          end;
827
          pl.PluginGUID := fPluginIdentifier();
828
        end;
829
 
830
        statusCode := fCheckLicense(nil);
831
        if statusCode.wCategory = UD2_STATUSCAT_FAILED then
832
        begin
833
          Errors.Add(Format(LNG_METHOD_FAILURE, [_ErrorLookup(statusCode), mnCheckLicense, dllFile]));
834
          Exit;
835
        end;
836
 
837
        ZeroMemory(@buf, cchBufferSize);
838
        statusCode := fPluginNameW(@buf, cchBufferSize, lngID);
839
             if statusCode.wCategory = UD2_STATUSCAT_SUCCESS   then pl.PluginName := PWideChar(@buf)
840
        else if statusCode.wCategory = UD2_STATUSCAT_NOT_AVAIL then pl.PluginName := ''
841
        else
842
        begin
843
          Errors.Add(Format(LNG_METHOD_FAILURE, [_ErrorLookup(statusCode), mnPluginNameW, dllFile]));
844
          Exit;
845
        end;
846
 
847
        ZeroMemory(@buf, cchBufferSize);
848
        statusCode := fPluginVendorW(@buf, cchBufferSize, lngID);
849
             if statusCode.wCategory = UD2_STATUSCAT_SUCCESS   then pl.PluginVendor := PWideChar(@buf)
850
        else if statusCode.wCategory = UD2_STATUSCAT_NOT_AVAIL then pl.PluginVendor := ''
851
        else
852
        begin
853
          Errors.Add(Format(LNG_METHOD_FAILURE, [_ErrorLookup(statusCode), mnPluginVendorW, dllFile]));
854
          Exit;
855
        end;
856
 
857
        ZeroMemory(@buf, cchBufferSize);
858
        statusCode := fPluginVersionW(@buf, cchBufferSize, lngID);
859
             if statusCode.wCategory = UD2_STATUSCAT_SUCCESS   then pl.PluginVersion := PWideChar(@buf)
860
        else if statusCode.wCategory = UD2_STATUSCAT_NOT_AVAIL then pl.PluginVersion := ''
861
        else
862
        begin
863
          Errors.Add(Format(LNG_METHOD_FAILURE, [_ErrorLookup(statusCode), mnPluginVersionW, dllFile]));
864
          Exit;
865
        end;
866
 
867
        ZeroMemory(@buf, cchBufferSize);
868
        statusCode := fIdentificationMethodNameW(@buf, cchBufferSize);
869
             if statusCode.wCategory = UD2_STATUSCAT_SUCCESS   then pl.IdentificationMethodName := PWideChar(@buf)
870
        else if statusCode.wCategory = UD2_STATUSCAT_NOT_AVAIL then pl.IdentificationMethodName := ''
871
        else
872
        begin
873
          Errors.Add(Format(LNG_METHOD_FAILURE, [_ErrorLookup(statusCode), mnIdentificationMethodNameW, dllFile]));
874
          Exit;
875
        end;
876
 
877
        ZeroMemory(@buf, cchBufferSize);
878
        statusCode := UD2_STATUS_FAILURE_NO_RETURNED_VALUE; // This status will be used when the DLL does not return anything (which is an error by the developer)
83 daniel-mar 879
        if useDynamicData then
880
        begin
881
          statusCode := fDynamicIdentificationStringW(@buf, cchBufferSize, PWideChar(dynamicData));
882
        end
883
        else
884
        begin
885
          statusCode := fIdentificationStringW(@buf, cchBufferSize);
886
        end;
82 daniel-mar 887
        pl.IdentificationProcedureStatusCode := statusCode;
888
        pl.IdentificationProcedureStatusCodeDescribed := _ErrorLookup(statusCode);
889
        if statusCode.wCategory = UD2_STATUSCAT_SUCCESS then
890
        begin
891
          sIdentifier := PWideChar(@buf);
892
          if UD2_STATUS_Equal(statusCode, UD2_STATUS_OK_MULTILINE, false) then
893
          begin
894
            // Multiple identifiers (e.g. multiple MAC addresses are delimited via UD2_MULTIPLE_ITEMS_DELIMITER)
83 daniel-mar 895
            SetLength(ResultIdentifiers, 0);
896
            ResultIdentifiers := SplitString(UD2_MULTIPLE_ITEMS_DELIMITER, sIdentifier);
897
            for i := Low(ResultIdentifiers) to High(ResultIdentifiers) do
82 daniel-mar 898
            begin
83 daniel-mar 899
              pl.AddIdentification(ResultIdentifiers[i]);
82 daniel-mar 900
            end;
901
          end
902
          else
903
          begin
904
            pl.AddIdentification(sIdentifier);
83 daniel-mar 905
 
906
            SetLength(ResultIdentifiers, 1);
907
            ResultIdentifiers[0] := sIdentifier;
82 daniel-mar 908
          end;
909
        end
910
        else if statusCode.wCategory <> UD2_STATUSCAT_NOT_AVAIL then
911
        begin
912
          if _AutoOSNotSupportedMode >= 3 then
913
          begin
914
            _OverwriteStatusToOSNotSupported;
915
            Exit;
916
          end;
917
 
918
          // Errors.Add(Format(LNG_METHOD_FAILURE, [_ErrorLookup(statusCode), mnIdentificationStringW, dllFile]));
919
          Errors.Add(Format(LNG_METHOD_FAILURE, [pl.IdentificationProcedureStatusCodeDescribed, mnIdentificationStringW, dllFile]));
920
          Exit;
921
        end;
922
 
923
        result := true;
924
      finally
925
        if not result and Assigned(pl) then FreeAndNil(pl);
926
        FreeLibrary(dllHandle);
927
      end;
928
    finally
929
      UD2_SetThreadErrorMode(bakErrorMode, nil);
930
 
931
      if result then
69 daniel-mar 932
      begin
82 daniel-mar 933
        endtime := GetTickCount;
934
        time := endtime - starttime;
935
        if endtime < starttime then time := High(Cardinal) - time;
936
        pl.time := time;
69 daniel-mar 937
      end;
82 daniel-mar 938
    end;
939
  except
940
    // TODO: when an exception happens in a cdecl DLL, then this code is somehow not
941
    // executed. Probably the memory is corrupted. Anyway, a cdecl DLL shall NEVER
942
    // raise an Exception.
943
    on E: Exception do
69 daniel-mar 944
    begin
82 daniel-mar 945
      Errors.Add(Format(LNG_EXCEPTION, [dllFile, E.ClassName, E.Message]));
69 daniel-mar 946
      Exit;
947
    end;
948
  end;
949
end;
950
 
68 daniel-mar 951
end.