@@ -11,6 +11,20 @@ interface
1111 RALCripto, RALCriptoAES, RALStream, RALCompress, RALConsts;
1212
1313type
14+ TRALCookieSiteScope = (cssLax, cssNone, cssStrict);
15+
16+ TRALCookie = record
17+ Name : StringRAL;
18+ Value : StringRAL;
19+ Domain: StringRAL;
20+ Path: StringRAL;
21+ Expires: TDateTime;
22+ MaxAge: Int64;
23+ HttpOnly: Boolean;
24+ SessionOnly: Boolean;
25+ Secure: Boolean;
26+ SameSite: TRALCookieSiteScope;
27+ end ;
1428
1529 { TRALParam }
1630
@@ -234,13 +248,155 @@ TEnumerator = class
234248 write FContentDispositionInline;
235249 end ;
236250
251+ function GetCookieText (ACookie: TRALCookie): StringRAL;
252+ function GetRALCookieFromText (ACookieString: StringRAL): TRALCookie;
253+ function GetRALCookieFromParam (AParamName: StringRAL; AParams: TRALParams): TRALCookie;
254+
237255implementation
238256
239257{ TRALParam }
240258
241259uses
242260 RALJson;
243261
262+ function DateTimeToCookieExpireDate (ADateTime: TDateTime): StringRAL;
263+ const
264+ HTTPMonths: array [1 ..12 ] of string[3 ] = (
265+ ' Jan' , ' Feb' , ' Mar' , ' Apr' ,
266+ ' May' , ' Jun' , ' Jul' , ' Aug' ,
267+ ' Sep' , ' Oct' , ' Nov' , ' Dec' );
268+ HTTPDays: array [1 ..7 ] of string[3 ] = (
269+ ' Sun' , ' Mon' , ' Tue' , ' Wed' ,
270+ ' Thu' , ' Fri' , ' Sat' );
271+
272+ DateFormat = ' "%s", dd "%s" yyyy hh:nn:ss' ;
273+ Expire = ' %s GMT' ;
274+ var
275+ vInt: integer;
276+ vYear, vMonth, vDay: Word;
277+ vExpire, vValue : StringRAL;
278+ test: String;
279+ begin
280+ // Dia da semana e nome do mês precisam ter a 1a letra maiúscula
281+ ADateTime := RALDateTimeToGMT(ADateTime);
282+ DecodeDate(ADateTime, vYear, vMonth, vDay);
283+
284+ vExpire := FormatDateTime(DateFormat, ADateTime);
285+ vExpire := Format(vExpire, [HTTPDays[DayOfWeek(ADateTime)], HTTPMonths[vMonth]]);
286+ vExpire := Format(Expire, [vExpire]);
287+ Result := vExpire;
288+ // Result := 'Mon, 27 Jul 2026 14:00:00 GMT'
289+ end ;
290+
291+ function GetCookieText (ACookie: TRALCookie): StringRAL;
292+ begin
293+ Result := ACookie.Name + ' =' + ACookie.Value ;
294+
295+ if ACookie.Domain <> ' ' then
296+ Result := Result + ' ; Domain=' + ACookie.Domain;
297+
298+ if ACookie.Path <> ' ' then
299+ Result := Result + ' ; Path=' + ACookie.Path;
300+
301+ if (not ACookie.SessionOnly) and (ACookie.Expires <> 0 ) then
302+ Result := Result + ' ; Expires=' + DateTimeToCookieExpireDate(ACookie.Expires);
303+
304+ if ACookie.Secure then
305+ Result := Result + ' ; Secure' ;
306+
307+ if ACookie.HttpOnly then
308+ Result := Result + ' ; HttpOnly' ;
309+
310+ case ACookie.SameSite of
311+ cssNone:
312+ if ACookie.Secure then
313+ Result := Result + ' ; SameSite=None' ;
314+ cssStrict:
315+ Result := Result + ' ; SameSite=Strict' ;
316+ end ;
317+ end ;
318+
319+ function GetRALCookieFromText (ACookieString: StringRAL): TRALCookie;
320+ var
321+ Start, P, EqPos, Len: Integer;
322+ S, Part, Name , Value : StringRAL;
323+ begin
324+ FillChar(Result, SizeOf(Result), 0 );
325+
326+ S := StringReplace(ACookieString, ' ; ' , ' ;' , [rfReplaceAll]);
327+ Len := Length(S);
328+ if Len = 0 then
329+ Exit;
330+
331+ Start := 1 ;
332+ while Start <= Len do
333+ begin
334+ // Encontra o próximo ';'
335+ P := Start;
336+ while (P <= Len) and (S[P] <> ' ;' ) do
337+ Inc(P);
338+
339+ // Extrai o trecho atual (já sem espaço extra por causa do Replace)
340+ Part := Copy(S, Start, P - Start);
341+
342+ // Avança para o próximo
343+ Start := P + 1 ;
344+
345+ if Part = ' ' then
346+ Continue;
347+
348+ EqPos := Pos(' =' , Part);
349+ if EqPos > 0 then
350+ begin
351+ Name := Copy(Part, 1 , EqPos - 1 );
352+ Value := Copy(Part, EqPos + 1 , MaxInt);
353+ end
354+ else
355+ begin
356+ Name := Part;
357+ Value := ' ' ;
358+ end ;
359+
360+ // Comparações case-sensitive como no original (pode trocar por SameText se quiser case-insensitive)
361+ if SameText(Name , ' HttpOnly' ) then
362+ Result.HttpOnly := True
363+ else if SameText(Name , ' Secure' ) then
364+ Result.Secure := True
365+ else if SameText(Name , ' Path' ) then
366+ Result.Path := Value
367+ else if SameText(Name , ' Domain' ) then
368+ Result.Domain := Value
369+ else if SameText(Name , ' SameSite' ) then
370+ begin
371+ if SameText(Value , ' None' ) then
372+ Result.SameSite := cssNone
373+ else if SameText(Value , ' Lax' ) then
374+ Result.SameSite := cssLax
375+ else if SameText(Value , ' Strict' ) then
376+ Result.SameSite := cssStrict;
377+ end
378+ else if SameText(Name , ' Expires' ) then
379+ Result.Expires := HTTPDateTimeToDateTime(Value )
380+ else if SameText(Name , ' Max-Age' ) then
381+ Result.MaxAge := StrToInt64Def(Value , 0 )
382+ else
383+ begin
384+ // Primeiro (e único) name=value que sobra é o cookie propriamente dito
385+ Result.Name := Name ;
386+ Result.Value := Value ;
387+ end ;
388+ end ;
389+ end ;
390+
391+ function GetRALCookieFromParam (AParamName: StringRAL; AParams: TRALParams
392+ ): TRALCookie;
393+ var
394+ vCookieStr: StringRAL;
395+ begin
396+ vCookieStr := AParams.GetKind[AParamName, rpkCOOKIE].AsString;
397+ Result := GetRALCookieFromText(vCookieStr);
398+ end ;
399+
244400procedure TRALParam.Clone (ASource: TRALParam);
245401begin
246402 ASource.ContentDispositionInline := Self.ContentDispositionInline;
0 commit comments