File: cookie_util.h

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (480 lines) | stat: -rw-r--r-- 23,109 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef NET_COOKIES_COOKIE_UTIL_H_
#define NET_COOKIES_COOKIE_UTIL_H_

#include <optional>
#include <string>
#include <string_view>
#include <vector>

#include "base/functional/callback_forward.h"
#include "base/time/time.h"
#include "base/types/optional_ref.h"
#include "net/base/net_export.h"
#include "net/cookies/cookie_constants.h"
#include "net/cookies/cookie_options.h"
#include "net/cookies/cookie_setting_override.h"
#include "net/cookies/site_for_cookies.h"
#include "net/first_party_sets/first_party_set_metadata.h"
#include "net/first_party_sets/first_party_sets_cache_filter.h"
#include "net/storage_access_api/status.h"
#include "url/origin.h"

class GURL;

namespace net {

class CanonicalCookie;
class CookieAccessDelegate;
class CookieInclusionStatus;
class IsolationInfo;
class ParsedCookie;
class SchemefulSite;

struct CookieAccessResult;
struct CookieWithAccessResult;

using CookieList = std::vector<CanonicalCookie>;
using CookieAccessResultList = std::vector<CookieWithAccessResult>;

namespace cookie_util {

// Constants for use in VLOG
const int kVlogPerCookieMonster = 1;
const int kVlogSetCookies = 7;
const int kVlogGarbageCollection = 5;

// This enum must match the numbering for StorageAccessResult in
// histograms/metadata/storage/enums.xml. Do not reorder or remove items, only
// add new items at the end.
enum class StorageAccessResult {
  ACCESS_BLOCKED = 0,
  ACCESS_ALLOWED = 1,
  ACCESS_ALLOWED_STORAGE_ACCESS_GRANT = 2,
  // OBSOLETE_ACCESS_ALLOWED_FORCED = 3 /*(DEPRECATED)*/,
  ACCESS_ALLOWED_TOP_LEVEL_STORAGE_ACCESS_GRANT = 4,
  ACCESS_ALLOWED_3PCD_TRIAL = 5,
  ACCESS_ALLOWED_3PCD_METADATA_GRANT = 6,
  ACCESS_ALLOWED_3PCD_HEURISTICS_GRANT = 7,
  // ACCESS_ALLOWED_CORS_EXCEPTION = 8,  // Deprecated
  ACCESS_ALLOWED_TOP_LEVEL_3PCD_TRIAL = 9,
  ACCESS_ALLOWED_SCHEME = 10,
  ACCESS_ALLOWED_SANDBOX_VALUE = 11,
  kMaxValue = ACCESS_ALLOWED_SANDBOX_VALUE,
};

// This enum's values correspond to the values of the HTTP request header
// `Sec-Fetch-Storage-Access`, which is applied to cross-site requests.
enum class StorageAccessStatus {
  // Applies to context that does not have unpartitioned cookie access, and does
  // not have the `storage-access` permission.
  kNone = 0,
  // Applies to context that has `storage-access` permission, but has not opted
  // into using it; the context also does not have unpartitioned cookie access
  // through some other means.
  kInactive = 1,
  // Applies to context that has unpartitioned cookie access.
  kActive = 2
};

// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
// The values of this enum correspond to possible reasons a request's
// StorageAccessStatus may be absent (nullopt), as well as the possible values
// when it is non-nullopt.
//
// LINT.IfChange(StorageAccessStatusOutcome)
enum class StorageAccessStatusOutcome {
  // The feature is disabled.
  // kOmittedFeatureDisabled = 0, // Deprecated (feature is always enabled).
  // The request is same-site.
  kOmittedSameSite = 1,
  // The storage access status is `none`.
  kValueNone = 2,
  // The storage access status is `inactive`.
  kValueInactive = 3,
  // The storage access status is `active`.
  kValueActive = 4,
  kMaxValue = kValueActive
};
// LINT.ThenChange(//tools/metrics/histograms/metadata/storage/enums.xml:StorageAccessStatusOutcome)

// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
// The values of this enum correspond to possible reasons the
// `Sec-Fetch-Storage-Access` header may be omitted from a request, as well as
// the possible values of the header when it is included.
enum class SecFetchStorageAccessOutcome {
  // The request's storage access status is nullopt.
  kOmittedStatusMissing = 0,
  // The request's credentials mode is not "include".
  kOmittedRequestOmitsCredentials = 1,
  // The `Sec-Fetch-Storage-Access` header is included and has the value `none`.
  kValueNone = 2,
  // The `Sec-Fetch-Storage-Access` header is included and has the value
  // `inactive`.
  kValueInactive = 3,
  // The `Sec-Fetch-Storage-Access` header is included and has the value
  // `active`.
  kValueActive = 4,
  kMaxValue = kValueActive
};

// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
// The values of this enum correspond to the possible outcomes of a call to
// URLRequest::ShouldSetLoadWithStorageAccess().
//
// LINT.IfChange(ActivateStorageAccessLoadOutcome)
enum class ActivateStorageAccessLoadOutcome {
  // Applies when the `Activate-Storage-Access` header behavior is not enabled
  // under the existing feature flags or content settings.
  // kFailureHeaderDisabled = 0, // Deprecated (feature is always enabled).
  // Applies when a response includes the `Activate-Storage-Access: load`
  // header, but its corresponding request either has an omitted storage access
  // status, or has a storage access status of `none`.
  kFailureInvalidStatus = 1,
  // Applies when a response includes the `Activate-Storage-Access: load`
  // header, and that header is honored by the browser.
  kSuccess = 2,
  kMaxValue = kSuccess
};
// LINT.ThenChange(//tools/metrics/histograms/metadata/storage/enums.xml:ActivateStorageAccessLoadOutcome)

// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
// The values of this enum correspond to the possible outcomes of a call to
// URLRequestHttpJob::NeedsRetryWithStorageAccess().
//
// LINT.IfChange(ActivateStorageAccessRetryOutcome)
enum class ActivateStorageAccessRetryOutcome {
  // Applies when the `Activate-Storage-Access` header behavior is not enabled
  // under the existing feature flags or content settings.
  // kFailureHeaderDisabled = 0, // Deprecated (feature is always enabled).
  // Applies when a response includes a well-formed
  // `Activate-Storage-Access: retry; ..." header, but the corresponding
  // request's `Sec-Fetch-Storage-Access` header is not `inactive`.
  kFailureIneffectiveRetry = 1,
  // Applies when a response includes a well-formed
  // "Activate-Storage-Access: retry; ..." header, and that header is honored
  // by the browser.
  kSuccess = 2,
  kMaxValue = kSuccess
};
// LINT.ThenChange(//tools/metrics/histograms/metadata/storage/enums.xml:ActivateStorageAccessRetryOutcome)

// Helper to fire telemetry indicating if a given request for storage was
// allowed or not by the provided |result|.
NET_EXPORT void FireStorageAccessHistogram(StorageAccessResult result);

// Returns the effective TLD+1 for a given host. This only makes sense for http
// and https schemes. For other schemes, the host will be returned unchanged
// (minus any leading period).
NET_EXPORT std::string GetEffectiveDomain(const std::string& scheme,
                                          const std::string& host);

// Determine the actual cookie domain based on the domain string passed
// (if any) and the URL from which the cookie came.
// On success returns either a
//   -host cookie domain (ex: "google.com")
//   -domain cookie domain (ex: ".google.com")
// On success, DomainIsHostOnly(url.host()) is DCHECKed. The URL's host must not
// begin with a '.' character.
NET_EXPORT std::optional<std::string> GetCookieDomainWithString(
    const GURL& url,
    std::string_view domain_string,
    CookieInclusionStatus& status);

// Returns true if a domain string represents a host-only cookie,
// i.e. it doesn't begin with a leading '.' character.
NET_EXPORT bool DomainIsHostOnly(const std::string& domain_string);

// If |cookie_domain| is nonempty and starts with a "." character, this returns
// the substring of |cookie_domain| without the leading dot. (Note only one
// leading dot is stripped, if there are multiple.) Otherwise it returns
// |cookie_domain|. This is useful for converting from CanonicalCookie's
// representation of a cookie domain to the RFC's notion of a cookie's domain.
NET_EXPORT std::string CookieDomainAsHost(const std::string& cookie_domain);

// Parses the string with the cookie expiration time (very forgivingly).
// Returns the "null" time on failure.
//
// If the expiration date is below or above the platform-specific range
// supported by Time::FromUTCExplodeded(), then this will return Time(1) or
// Time::Max(), respectively.
NET_EXPORT base::Time ParseCookieExpirationTime(std::string_view time_string);

// Returns the canonical path based on the specified url and path attribute
// value. Note that this method does not enforce character set or size
// checks on `path_string`.
NET_EXPORT std::string CanonPathWithString(const GURL& url,
                                           std::string_view path_string);

// Get a cookie's URL from it's domain, path, and source scheme.
// The first field can be the combined domain-and-host-only-flag (e.g. the
// string returned by CanonicalCookie::Domain()) as opposed to the domain
// attribute per RFC6265bis. The GURL is constructed after stripping off any
// leading dot.
// Note: the GURL returned by this method is not guaranteed to be valid.
NET_EXPORT GURL CookieDomainAndPathToURL(const std::string& domain,
                                         const std::string& path,
                                         const std::string& source_scheme);
NET_EXPORT GURL CookieDomainAndPathToURL(const std::string& domain,
                                         const std::string& path,
                                         bool is_https);
NET_EXPORT GURL CookieDomainAndPathToURL(const std::string& domain,
                                         const std::string& path,
                                         CookieSourceScheme source_scheme);

// Convenience for converting a cookie origin (domain and https pair) to a URL.
NET_EXPORT GURL CookieOriginToURL(const std::string& domain, bool is_https);

// Returns a URL that could have been the cookie's source.
// Not guaranteed to actually be the URL that set the cookie. Not guaranteed to
// be a valid GURL. Intended as a shim for SetCanonicalCookieAsync calls, where
// a source URL is required but only a source scheme may be available.
NET_EXPORT GURL SimulatedCookieSource(const CanonicalCookie& cookie,
                                      const std::string& source_scheme);

// Provisional evaluation of acceptability of setting secure cookies on
// `source_url` based only on the `source_url`'s scheme and whether it
// is a localhost URL.  If this returns kNonCryptographic, it may be upgraded to
// kTrustworthy by a CookieAccessDelegate when the cookie operation is being
// performed, as the delegate may have access to user settings like manually
// configured test domains which declare additional things trustworthy.
NET_EXPORT CookieAccessScheme ProvisionalAccessScheme(const GURL& source_url);

// |domain| is the output of cookie.Domain() for some cookie. This returns true
// if a |domain| indicates that the cookie can be accessed by |host|.
// See comment on CanonicalCookie::IsDomainMatch().
NET_EXPORT bool IsDomainMatch(const std::string& domain,
                              const std::string& host);

// Returns true if the given |url_path| path-matches |cookie_path|
// as described in section 5.1.4 in RFC 6265. This returns true if |cookie_path|
// and |url_path| are identical, or if |url_path| is a subdirectory of
// |cookie_path|.
NET_EXPORT bool IsOnPath(const std::string& cookie_path,
                         const std::string& url_path);

// Returns the CookiePrefix (or COOKIE_PREFIX_NONE if none) that
// applies to the given cookie |name|.
CookiePrefix GetCookiePrefix(const std::string& name);

// Returns true if the cookie does not violate any constraints imposed
// by the cookie name's prefix, as described in
// https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-13#name-cookie-name-prefixes
bool IsCookiePrefixValid(CookiePrefix prefix,
                         const GURL& url,
                         const ParsedCookie& parsed_cookie);
// As above. `secure`, `domain`, and `path` are the raw attribute values (i.e.
// as taken from a ParsedCookie), NOT in normalized form as represented in
// CookieBase.
NET_EXPORT_PRIVATE bool IsCookiePrefixValid(CookiePrefix prefix,
                                            const GURL& url,
                                            bool secure,
                                            std::string_view domain,
                                            std::string_view path);

// Returns true iff the cookie is a partitioned cookie with a nonce or that
// does not violate the semantics of the Partitioned attribute:
// - Must have the Secure attribute OR the cookie partition contains a nonce.
bool IsCookiePartitionedValid(const GURL& url,
                              const ParsedCookie& parsed_cookie,
                              bool partition_has_nonce);
bool IsCookiePartitionedValid(const GURL& url,
                              bool secure,
                              bool is_partitioned,
                              bool partition_has_nonce);

// A ParsedRequestCookie consists of the key and value of the cookie.
using ParsedRequestCookie = std::pair<std::string, std::string>;
using ParsedRequestCookies = std::vector<ParsedRequestCookie>;

// Assumes that |header_value| is the cookie header value of a HTTP Request
// following the cookie-string schema of RFC 6265, section 4.2.1, and returns
// cookie name/value pairs. If cookie values are presented in double quotes,
// these will appear in |parsed_cookies| as well. The cookie header can be
// written by non-Chromium consumers (such as extensions), so the header may not
// be well-formed.
NET_EXPORT void ParseRequestCookieLine(const std::string& header_value,
                                       ParsedRequestCookies* parsed_cookies);

// Writes all cookies of |parsed_cookies| into a HTTP Request header value
// that belongs to the "Cookie" header. The entries of |parsed_cookies| must
// already be appropriately escaped.
NET_EXPORT std::string SerializeRequestCookieLine(
    const ParsedRequestCookies& parsed_cookies);

// Determines which of the cookies for the request URL can be accessed, with
// respect to the SameSite attribute. This applies to looking up existing
// cookies for HTTP requests. For looking up cookies for non-HTTP APIs (i.e.,
// JavaScript), see ComputeSameSiteContextForScriptGet. For setting new cookies,
// see ComputeSameSiteContextForResponse and ComputeSameSiteContextForScriptSet.
//
// `url_chain` is a non-empty vector of URLs, the last of which is the current
// request URL. It represents the redirect chain of the current request. The
// redirect chain is used to calculate whether there has been a cross-site
// redirect. In order for a context to be deemed strictly same-site, there must
// not have been any cross-site redirects.
//
// `site_for_cookies` is the currently navigated to site that should be
// considered "first-party" for cookies.
//
// `initiator` is the origin ultimately responsible for getting the request
// issued. It may be different from `site_for_cookies`.
//
// std::nullopt for `initiator` denotes that the navigation was initiated by
// the user directly interacting with the browser UI, e.g. entering a URL
// or selecting a bookmark.
//
// `is_main_frame_navigation` is whether the request is for a navigation that
// targets the main frame or top-level browsing context. These requests may
// sometimes send SameSite=Lax cookies but not SameSite=Strict cookies.
//
// If `force_ignore_site_for_cookies` is specified, all SameSite cookies will be
// attached, i.e. this will return SAME_SITE_STRICT. This flag is set to true
// when the `site_for_cookies` is a chrome:// URL embedding a secure origin,
// among other scenarios.
// This is *not* set when the *initiator* is chrome-extension://,
// which is intentional, since it would be bad to let an extension arbitrarily
// redirect anywhere and bypass SameSite=Strict rules.
//
// See also documentation for corresponding methods on net::URLRequest.
//
// `http_method` is used to enforce the requirement that, in a context that's
// lax same-site but not strict same-site, SameSite=lax cookies be only sent
// when the method is "safe" in the RFC7231 section 4.2.1 sense.
NET_EXPORT CookieOptions::SameSiteCookieContext
ComputeSameSiteContextForRequest(const std::string& http_method,
                                 const std::vector<GURL>& url_chain,
                                 const SiteForCookies& site_for_cookies,
                                 const std::optional<url::Origin>& initiator,
                                 bool is_main_frame_navigation,
                                 bool force_ignore_site_for_cookies);

// As above, but applying for scripts. `initiator` here should be the initiator
// used when fetching the document.
// If `force_ignore_site_for_cookies` is true, this returns SAME_SITE_STRICT.
NET_EXPORT CookieOptions::SameSiteCookieContext
ComputeSameSiteContextForScriptGet(const GURL& url,
                                   const SiteForCookies& site_for_cookies,
                                   const std::optional<url::Origin>& initiator,
                                   bool force_ignore_site_for_cookies);

// Determines which of the cookies for the request URL can be set from a network
// response, with respect to the SameSite attribute. This will only return
// CROSS_SITE or SAME_SITE_LAX (cookie sets of SameSite=strict cookies are
// permitted in same contexts that sets of SameSite=lax cookies are).
// `url_chain` is a non-empty vector of URLs, the last of which is the current
// request URL. It represents the redirect chain of the current request. The
// redirect chain is used to calculate whether there has been a cross-site
// redirect.
// `is_main_frame_navigation` is whether the request was for a navigation that
// targets the main frame or top-level browsing context. Both SameSite=Lax and
// SameSite=Strict cookies may be set by any main frame navigation.
// If `force_ignore_site_for_cookies` is true, this returns SAME_SITE_LAX.
NET_EXPORT CookieOptions::SameSiteCookieContext
ComputeSameSiteContextForResponse(const std::vector<GURL>& url_chain,
                                  const SiteForCookies& site_for_cookies,
                                  const std::optional<url::Origin>& initiator,
                                  bool is_main_frame_navigation,
                                  bool force_ignore_site_for_cookies);

// Determines which of the cookies for `url` can be set from a script context,
// with respect to the SameSite attribute. This will only return CROSS_SITE or
// SAME_SITE_LAX (cookie sets of SameSite=strict cookies are permitted in same
// contexts that sets of SameSite=lax cookies are).
// If `force_ignore_site_for_cookies` is true, this returns SAME_SITE_LAX.
NET_EXPORT CookieOptions::SameSiteCookieContext
ComputeSameSiteContextForScriptSet(const GURL& url,
                                   const SiteForCookies& site_for_cookies,
                                   bool force_ignore_site_for_cookies);

// Determines which of the cookies for |url| can be accessed when fetching a
// subresources. This is either CROSS_SITE or SAME_SITE_STRICT,
// since the initiator for a subresource is the frame loading it.
NET_EXPORT CookieOptions::SameSiteCookieContext
// If |force_ignore_site_for_cookies| is true, this returns SAME_SITE_STRICT.
ComputeSameSiteContextForSubresource(const GURL& url,
                                     const SiteForCookies& site_for_cookies,
                                     bool force_ignore_site_for_cookies);

NET_EXPORT bool IsPortBoundCookiesEnabled();

NET_EXPORT bool IsSchemeBoundCookiesEnabled();

// Returns true if either portion of OBC is enabled.
NET_EXPORT bool IsOriginBoundCookiesPartiallyEnabled();

NET_EXPORT bool IsTimeLimitedInsecureCookiesEnabled();

// Computes the First-Party Sets metadata and cache match information.
// `isolation_info` must be fully populated.
//
// The result may be returned synchronously, or `callback` may be invoked
// asynchronously with the result. The callback will be invoked iff the return
// value is nullopt; i.e. a result will be provided via return value or
// callback, but not both, and not neither.
[[nodiscard]] NET_EXPORT std::optional<
    std::pair<FirstPartySetMetadata, FirstPartySetsCacheFilter::MatchInfo>>
ComputeFirstPartySetMetadataMaybeAsync(
    const SchemefulSite& request_site,
    const IsolationInfo& isolation_info,
    const CookieAccessDelegate* cookie_access_delegate,
    base::OnceCallback<void(FirstPartySetMetadata,
                            FirstPartySetsCacheFilter::MatchInfo)> callback);

// Converts a string representing the http request method to its enum
// representation.
NET_EXPORT CookieOptions::SameSiteCookieContext::ContextMetadata::HttpMethod
HttpMethodStringToEnum(const std::string& in);

// Takes a CookieAccessResult and returns a bool, returning true if the
// CookieInclusionStatus in CookieAccessResult was set to "include", else
// returning false.
//
// Can be used with SetCanonicalCookie when you don't need to know why a cookie
// was blocked, only whether it was blocked.
NET_EXPORT bool IsCookieAccessResultInclude(
    CookieAccessResult cookie_access_result);

// Turn a CookieAccessResultList into a CookieList by stripping out access
// results (for callers who only care about cookies).
NET_EXPORT CookieList
StripAccessResults(const CookieAccessResultList& cookie_access_result_list);

// Records port related metrics from Omnibox navigations.
NET_EXPORT void RecordCookiePortOmniboxHistograms(const GURL& url);

// Checks invariants that should be upheld w.r.t. the included and excluded
// cookies. Namely: the included cookies should be elements of
// `included_cookies`; excluded cookies should be elements of
// `excluded_cookies`; and included cookies should be in the correct sorted
// order.
NET_EXPORT void DCheckIncludedAndExcludedCookieLists(
    const CookieAccessResultList& included_cookies,
    const CookieAccessResultList& excluded_cookies);

// Returns the default third-party cookie blocking setting, which is false
// unless you enable ForceThirdPartyCookieBlocking with the command line switch
// --test-third-party-cookie-phaseout.
NET_EXPORT bool IsForceThirdPartyCookieBlockingEnabled();

// Indicates whether the first hop in a request should have the
// kStorageAccessGrantEligible override.
[[nodiscard]] NET_EXPORT bool ShouldAddInitialStorageAccessApiOverride(
    const GURL& url,
    StorageAccessApiStatus api_status,
    base::optional_ref<const url::Origin> request_initiator,
    bool emit_metrics,
    bool credentials_mode_include);

}  // namespace cookie_util

}  // namespace net

#endif  // NET_COOKIES_COOKIE_UTIL_H_