Subversion Repositories userdetect2

Rev

Rev 69 | Go to most recent revision | Details | 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
 
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.