Subversion Repositories oidplus

Rev

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

  1. unit WEID;
  2.  
  3. (*
  4.  * WEID<=>OID Converter for TurboPascal
  5.  * (c) Webfan.de, ViaThinkSoft
  6.  * Revision 2022-02-22
  7.  *)
  8.  
  9. (*
  10.   What is a WEID?
  11.     A WEID (WEhowski IDentifier) is an alternative representation of an
  12.      OID (Object IDentifier) defined by Till Wehowski.
  13.      In OIDs, arcs are in decimal base 10. In WEIDs, the arcs are in base 36.
  14.      Also, each WEID has a check digit at the end (called WeLohn Check Digit).
  15.  
  16.   Changes in the December 2021 definition by Daniel Marschall:
  17.      - There are several classes of WEIDs which have different OID bases:
  18.            "Class C" WEID:  weid:EXAMPLE-3      (base .1.3.6.1.4.1.37553.8.)
  19.                             oid:1.3.6.1.4.1.37553.8.32488192274
  20.            "Class B" WEID:  weid:pen:SX0-7PR-6  (base .1.3.6.1.4.1.)
  21.                             oid:1.3.6.1.4.1.37476.9999
  22.            "Class A" WEID:  weid:root:2-RR-2    (base .)
  23.                             oid:2.999
  24.      - The namespace (weid:, weid:pen:, weid:root:) is now case insensitive.
  25.      - Padding with '0' characters is valid (e.g. weid:000EXAMPLE-3)
  26.        The paddings do not count into the WeLuhn check-digit.
  27. *)
  28.  
  29. interface
  30.  
  31. (*
  32. Translates a weid to an oid
  33. "weid:EXAMPLE-3" becomes "1.3.6.1.4.1.37553.8.32488192274"
  34. If it failed (e.g. wrong namespace, wrong checksum, etc.) then false is returned.
  35. If the weid ends with '?', then it will be replaced with the checksum,
  36. e.g. weid:EXAMPLE-? becomes weid:EXAMPLE-3
  37. *)
  38. function WeidToOid(var weid: string): string;
  39.  
  40. (*
  41. Converts an OID to WEID
  42. "1.3.6.1.4.1.37553.8.32488192274" becomes "weid:EXAMPLE-3"
  43. *)
  44. function OidToWeid(oid: string): string;
  45.  
  46. implementation
  47.  
  48. uses
  49.   VtsFuncs;
  50.  
  51. function weLuhnGetCheckDigit(s: string): integer;
  52. var
  53.   p: integer;
  54.   wrkstr: string;
  55.   c: Char;
  56.   i: Integer;
  57.   sum: integer;
  58.   nbdigits: Integer;
  59.   parity: Integer;
  60.   n: Integer;
  61.   digit: Integer;
  62. begin
  63.   (* Padding zeros don't count to the check digit (December 2021) *)
  64.   s := '-' + s + '-';
  65.   while Pos('-0', s) > 0 do
  66.   begin
  67.     s := StringReplace(s, '-0-', #1);
  68.     s := StringReplace(s, '-0', '-');
  69.   end;
  70.   s := StringReplace(s, #1, '-0-');
  71.   s := Copy(s, 2, Length(s)-2);
  72.  
  73.   (* remove separators of the WEID string *)
  74.   wrkstr := StringReplace(s, '-', '');
  75.  
  76.   (* Replace 'a' with '10', 'b' with '11', etc. *)
  77.   for c := 'A' to 'Z' do
  78.   begin
  79.     wrkstr := StringReplace(wrkstr, c, IntToStr(Ord(c)-Ord('A')+10));
  80.   end;
  81.  
  82.   (* At the end, wrkstr should only contain digits! Verify it! *)
  83.   for i := 1 to Length(wrkstr) do
  84.   begin
  85.     if not (wrkstr[i] in ['0'..'9']) then
  86.     begin
  87.       weLuhnGetCheckDigit := -1;
  88.       exit;
  89.     end;
  90.   end;
  91.  
  92.   (* Now do the standard Luhn algorithm *)
  93.   nbdigits := Length(wrkstr);
  94.   parity := nbdigits and 1; (* mod 2 *)
  95.   sum := 0;
  96.   for n := nbdigits-1 downto 0 do
  97.   begin
  98.     digit := StrToInt(wrkstr[n+1]);
  99.     if (n and 1) <> parity then digit := digit * 2;
  100.     if digit > 9 then digit := digit - 9;
  101.     sum := sum + digit;
  102.   end;
  103.  
  104.   if sum mod 10 = 0 then
  105.     weLuhnGetCheckDigit := 0
  106.   else
  107.     weLuhnGetCheckDigit := 10 - (sum mod 10);
  108. end;
  109.  
  110. function WeidToOid(var weid: string): string;
  111. var
  112.   base: string;
  113.   namespace: string;
  114.   p: integer;
  115.   rest: string;
  116.   actual_checksum: string;
  117.   expected_checksum: integer;
  118.   complete: string;
  119.   oidstr: string;
  120.   arc: string;
  121. begin
  122.   p := LastCharPos(weid,':');
  123.   namespace := Copy(weid, 1, p);
  124.   rest := Copy(weid, p+1, Length(weid)-p);
  125.  
  126.   namespace := LowerCase(namespace); (* namespace is case insensitive *)
  127.   if namespace = 'weid:' then
  128.   begin
  129.     (* Class C *)
  130.     base := '1-3-6-1-4-1-SZ5-8';
  131.   end
  132.   else if namespace = 'weid:pen:' then
  133.   begin
  134.     (* Class B *)
  135.     base := '1-3-6-1-4-1';
  136.   end
  137.   else if namespace = 'weid:root:' then
  138.   begin
  139.     (* Class A *)
  140.     base := '';
  141.   end
  142.   else
  143.   begin
  144.     (* Wrong namespace *)
  145.     WeidToOid := '';
  146.     Exit;
  147.   end;
  148.  
  149.   weid := rest;
  150.  
  151.   if base <> '' then
  152.     complete := base + '-' + weid
  153.   else
  154.     complete := weid;
  155.   p := LastCharPos(complete, '-');
  156.   actual_checksum := Copy(complete, p+1, 1);
  157.   complete := Copy(complete, 1, p-1);
  158.   expected_checksum := weLuhnGetCheckDigit(complete);
  159.   if (actual_checksum <> '?') then
  160.   begin
  161.     if actual_checksum <> IntToStr(expected_checksum) then
  162.     begin
  163.       WeidToOid := ''; (* wrong checksum *)
  164.       Exit;
  165.     end;
  166.   end
  167.   else
  168.   begin
  169.     (* If checksum is '?', it will be replaced by the actual checksum, *)
  170.     (* e.g. weid:EXAMPLE-? becomes weid:EXAMPLE-3                      *)
  171.     weid := StringReplace(weid, '?', IntToStr(expected_checksum));
  172.   end;
  173.  
  174.   oidstr := '';
  175.   while true do
  176.   begin
  177.     p := Pos('-', complete);
  178.     if p = 0 then p := Length(complete)+1;
  179.     arc := Copy(complete, 1, p-1);
  180.     Delete(complete, 1, p);
  181.     oidstr := oidstr + base_convert_bigint(arc, 36, 10) + '.';
  182.     if complete = '' then break;
  183.   end;
  184.   oidstr := Copy(oidstr, 1, Length(oidstr)-1);
  185.  
  186.   weid := LowerCase(namespace) + UpperCase(weid); (* add namespace again *)
  187.  
  188.   WeidToOid := oidstr;
  189. end;
  190.  
  191. function OidToWeid(oid: string): string;
  192. var
  193.   is_class_a: boolean;
  194.   is_class_b: boolean;
  195.   is_class_c: boolean;
  196.   weidstr: string;
  197.   checksum: string;
  198.   namespace: string;
  199.   p: Integer;
  200.   cd: Integer;
  201.   res: string;
  202. begin
  203.   if Copy(oid,1,1) = '.' then
  204.     Delete(oid,1,1); (* remove leading dot *)
  205.  
  206.   if oid <> '' then
  207.   begin
  208.     weidstr := '';
  209.     while true do
  210.     begin
  211.       p := Pos('.', oid);
  212.       if p = 1 then
  213.       begin
  214.         Delete(oid, 1, 1);
  215.       end
  216.       else if p > 0 then
  217.       begin
  218.         weidstr := weidstr + base_convert_bigint(Copy(oid, 1, p-1),10,36) + '-';
  219.         Delete(oid, 1, p);
  220.       end
  221.       else
  222.       begin
  223.         weidstr := weidstr + base_convert_bigint(oid,10,36) + '-';
  224.         break;
  225.       end;
  226.     end;
  227.     weidstr := Copy(weidstr, 1, Length(weidstr)-1);
  228.   end
  229.   else
  230.   begin
  231.     weidstr := '';
  232.   end;
  233.  
  234.   is_class_c := (Pos('1-3-6-1-4-1-SZ5-8-', weidstr) = 1) or
  235.                 (weidstr = '1-3-6-1-4-1-SZ5-8');
  236.   is_class_b := ((Pos('1-3-6-1-4-1-', weidstr) = 1) or
  237.                 (weidstr = '1-3-6-1-4-1'))
  238.                 and not is_class_c;
  239.   is_class_a := not is_class_b and not is_class_c;
  240.  
  241.   cd := weLuhnGetCheckDigit(weidstr);
  242.   if cd < 0 then
  243.   begin
  244.     OidToWeid := weidstr;
  245.     exit;
  246.   end;
  247.   checksum := IntToStr(cd);
  248.  
  249.   if is_class_c then
  250.   begin
  251.     Delete(weidstr, 1, Length('1-3-6-1-4-1-SZ5-8-'));
  252.     namespace := 'weid:';
  253.   end
  254.   else if is_class_b then
  255.   begin
  256.     Delete(weidstr, 1, Length('1-3-6-1-4-1-'));
  257.     namespace := 'weid:pen:';
  258.   end
  259.   else if is_class_a then
  260.   begin
  261.     (* weidstr stays *)
  262.     namespace := 'weid:root:';
  263.   end
  264.   else
  265.   begin
  266.     (* should not happen *)
  267.     OidToWeid := '';
  268.     Exit;
  269.   end;
  270.  
  271.   res := namespace;
  272.   if weidstr = '' then
  273.     res := res + checksum
  274.   else
  275.     res := res + weidstr + '-' + checksum;
  276.   OidToWeid := res;
  277. end;
  278.  
  279. end.
  280.