Changeset 199


Ignore:
Timestamp:
Jan 28, 2026, 10:24:15 AM (4 hours ago)
Author:
chronos
Message:
  • Fixed: Handling escaped semicolon in indexed property values.
Location:
trunk
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/Packages/VCard/VCard.pas

    r173 r199  
    858858function TContactProperty.GetValueItem(Index: Integer): string;
    859859var
    860   List: TStringList;
    861 begin
    862   List := TStringList.Create;
    863   try
    864     List.Delimiter := ';';
    865     List.NameValueSeparator := '=';
    866     List.StrictDelimiter := True;
    867     List.DelimitedText := Value;
    868     if Index < List.Count then
    869       Result := List.Strings[Index]
    870       else Result := '';
    871   finally
    872     List.Free;
    873   end;
     860  I: Integer;
     861  P: Integer;
     862  CurrentIndex: Integer;
     863  CurrentIndexStart: Integer;
     864  CurrentIndexEnd: Integer;
     865begin
     866  CurrentIndex := 0;
     867  CurrentIndexStart := 1;
     868  CurrentIndexEnd := Length(Value);
     869  P := 1;
     870  repeat
     871    I := Pos(';', Value, P);
     872    if I > 0 then begin
     873      if (I > 1) and (Value[I - 1] = '\') then begin
     874        P := I + 1;
     875        Continue;
     876      end else begin
     877        CurrentIndexEnd := I - 1;
     878        if CurrentIndex = Index then begin
     879          Break;
     880        end else begin
     881          P := I + 1;
     882          Inc(CurrentIndex);
     883          CurrentIndexStart := P;
     884          CurrentIndexEnd := Length(Value);
     885        end;
     886      end;
     887    end else begin
     888      Break;
     889    end;
     890  until False;
     891  if Index = CurrentIndex then begin
     892    Result := Copy(Value, CurrentIndexStart, CurrentIndexEnd - CurrentIndexStart + 1);
     893  end else Result := '';
    874894end;
    875895
     
    895915procedure TContactProperty.SetValueItem(Index: Integer; AValue: string);
    896916var
    897   List: TStringList;
    898 begin
    899   List := TStringList.Create;
    900   try
    901     List.Delimiter := ';';
    902     List.NameValueSeparator := '=';
    903     List.StrictDelimiter := True;
    904     List.DelimitedText := Value;
    905 
    906     // Extend subitems count
    907     while List.Count <= Index do
    908       List.Add('');
    909 
    910     List.Strings[Index] := AValue;
    911 
    912     // Remove empty items
    913     while (List.Count > 0) and (List.Strings[List.Count - 1] = '') do
    914       List.Delete(List.Count - 1);
    915 
    916     Value := List.DelimitedText;
    917   finally
    918     List.Free;
     917  I: Integer;
     918  P: Integer;
     919  CurrentIndex: Integer;
     920  CurrentIndexStart: Integer;
     921  CurrentIndexEnd: Integer;
     922begin
     923  CurrentIndex := 0;
     924  CurrentIndexStart := 1;
     925  CurrentIndexEnd := Length(Value);
     926  P := 1;
     927  repeat
     928    I := Pos(';', Value, P);
     929    if I > 0 then begin
     930      if (I > 1) and (Value[I - 1] = '\') then begin
     931        P := I + 1;
     932        Continue;
     933      end else begin
     934        CurrentIndexEnd := I - 1;
     935        if CurrentIndex = Index then begin
     936          Break;
     937        end else begin
     938          P := I + 1;
     939          Inc(CurrentIndex);
     940          CurrentIndexStart := P;
     941          CurrentIndexEnd := Length(Value);
     942        end;
     943      end;
     944    end else begin
     945      Break;
     946    end;
     947  until False;
     948  if Index = CurrentIndex then begin
     949    Value := Copy(Value, 1, CurrentIndexStart - 1) +
     950      AValue + Copy(Value, CurrentIndexEnd + 1, MaxInt);
     951  end else begin
     952    for I := CurrentIndex + 1 to Index do begin
     953      Value := Value + ';';
     954      if I = Index then Value := Value + AValue;
     955    end;
    919956  end;
    920957end;
  • trunk/Test.pas

    r168 r199  
    1919  end;
    2020
    21   { TTestCaseCheckProperty }
    22 
    23   TTestCaseCheckProperty = class(TTestCase)
    24     Input: string;
     21  { TTestCasePropertyRead }
     22
     23  TTestCasePropertyRead = class(TTestCase)
     24    Input: string;
     25    ContactIndex: Integer;
     26    Index: TContactFieldIndex;
     27    Value: string;
     28    procedure Run; override;
     29  end;
     30
     31  { TTestCasePropertyWrite }
     32
     33  TTestCasePropertyWrite = class(TTestCase)
     34    Input: string;
     35    Output: string;
    2536    ContactIndex: Integer;
    2637    Index: TContactFieldIndex;
     
    140151end;
    141152
    142 { TTestCaseCheckProperty }
    143 
    144 procedure TTestCaseCheckProperty.Run;
     153{ TTestCasePropertyRead }
     154
     155procedure TTestCasePropertyRead.Run;
    145156var
    146157  Lines: TStringList;
     
    169180end;
    170181
     182{ TTestCasePropertyWrite }
     183
     184procedure TTestCasePropertyWrite.Run;
     185var
     186  Lines: TStringList;
     187begin
     188  Lines := TStringList.Create;
     189  try
     190    with TVCardFile.Create(nil) do
     191    try
     192      Lines.Text := Input;
     193      VCard.LoadFromStrings(Lines);
     194      if ContactIndex < VCard.Contacts.Count then begin
     195        VCard.Contacts[ContactIndex].Fields[Index] := Value;
     196      end else Fail;
     197      Lines.Clear;
     198      VCard.SaveToStrings(Lines);
     199      Log := SExpected + LineEnding +
     200        '"' + Output + '"' + LineEnding + LineEnding +
     201        SOutput + LineEnding +
     202        '"' + Lines.Text + '"';
     203      Evaluate(Lines.Text = Output);
     204    finally
     205      Free;
     206    end;
     207  finally
     208    Lines.Free;
     209  end;
     210end;
     211
    171212{ TTestCaseLoadSave }
    172213
  • trunk/TestCases.pas

    r169 r199  
    105105      Output := '';
    106106    end;
    107     with TTestCaseCheckProperty(AddNew('Property FN', TTestCaseCheckProperty)) do begin
     107    with TTestCasePropertyRead(AddNew('Property FN', TTestCasePropertyRead)) do begin
    108108      Index := cfFullName;
    109109      Value := 'Name Surname';
    110110      Input := BeginEnd('FN:' + Value + VCardLineEnding);
    111111    end;
    112     with TTestCaseCheckProperty(AddNew('Escaped new lines in text', TTestCaseCheckProperty)) do begin
     112    with TTestCasePropertyRead(AddNew('Escaped new lines in text', TTestCasePropertyRead)) do begin
    113113      Index := cfNote;
    114114      Value := 'Line' + #13#10 + 'Line';
    115115      Input := BeginEnd('NOTE:Line\nLine' + VCardLineEnding);
    116116    end;
    117     with TTestCaseCheckProperty(AddNew('Compound value', TTestCaseCheckProperty)) do begin
     117    with TTestCasePropertyRead(AddNew('Compound value', TTestCasePropertyRead)) do begin
    118118      Index := cfFirstName;
    119119      Value := 'FirstName';
    120120      Input := BeginEnd('N:Surname;FirstName;;;' + VCardLineEnding);
    121121    end;
    122     with TTestCaseCheckProperty(AddNew('Quoted-printable special symbols', TTestCaseCheckProperty)) do begin
     122    with TTestCasePropertyRead(AddNew('Escaped semicolon in name get 0', TTestCasePropertyRead)) do begin
     123      Index := cfLastName;
     124      Value := 'Surname;Surname';
     125      Input := BeginEnd('N:Surname\;Surname;Name\;Name' + VCardLineEnding);
     126    end;
     127    with TTestCasePropertyRead(AddNew('Escaped semicolon in name get 1', TTestCasePropertyRead)) do begin
     128      Index := cfFirstName;
     129      Value := 'Name;Name';
     130      Input := BeginEnd('N:Surname\;Surname;Name\;Name' + VCardLineEnding);
     131    end;
     132    with TTestCasePropertyRead(AddNew('Escaped semicolon in name get 2', TTestCasePropertyRead)) do begin
     133      Index := cfMiddleName;
     134      Value := '';
     135      Input := BeginEnd('N:Surname\;Surname;Name\;Name' + VCardLineEnding);
     136    end;
     137    with TTestCasePropertyWrite(AddNew('Escaped semicolon in name set 0', TTestCasePropertyWrite)) do begin
     138      Input := BeginEnd('N:Surname\;Surname;Name\;Name' + VCardLineEnding);
     139      Index := cfLastName;
     140      Value := 'Surname2;Surname2';
     141      Output := BeginEnd('N:Surname2\;Surname2;Name\;Name' + VCardLineEnding);
     142    end;
     143    with TTestCasePropertyWrite(AddNew('Escaped semicolon in name set 1', TTestCasePropertyWrite)) do begin
     144      Input := BeginEnd('N:Surname\;Surname;Name\;Name' + VCardLineEnding);
     145      Index := cfFirstName;
     146      Value := 'Name2;Name2';
     147      Output := BeginEnd('N:Surname\;Surname;Name2\;Name2' + VCardLineEnding);
     148    end;
     149    with TTestCasePropertyWrite(AddNew('Escaped semicolon in name set 2', TTestCasePropertyWrite)) do begin
     150      Input := BeginEnd('N:Surname\;Surname;Name\;Name' + VCardLineEnding);
     151      Index := cfMiddleName;
     152      Value := 'Middle2;Middle2';
     153      Output := BeginEnd('N:Surname\;Surname;Name\;Name;Middle2\;Middle2' + VCardLineEnding);
     154    end;
     155    with TTestCasePropertyRead(AddNew('Quoted-printable special symbols', TTestCasePropertyRead)) do begin
    123156      Index := cfFullName;
    124157      Value := 'Jméno Příjmení';
     
    126159        'FN;ENCODING=QUOTED-PRINTABLE:Jm=C3=A9no=20P=C5=99=C3=ADjmen=C3=AD' + VCardLineEnding);
    127160    end;
    128     with TTestCaseCheckProperty(AddNew('Base64 special symbols', TTestCaseCheckProperty)) do begin
     161    with TTestCasePropertyRead(AddNew('Base64 special symbols', TTestCasePropertyRead)) do begin
    129162      Index := cfFullName;
    130163      Value := 'Jméno Příjmení';
Note: See TracChangeset for help on using the changeset viewer.