Coverage Report

Created: 2025-07-23 06:33

/src/php-src/ext/uri/php_lexbor.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
   +----------------------------------------------------------------------+
3
   | Copyright (c) The PHP Group                                          |
4
   +----------------------------------------------------------------------+
5
   | This source file is subject to version 3.01 of the PHP license,      |
6
   | that is bundled with this package in the file LICENSE, and is        |
7
   | available through the world-wide-web at the following url:           |
8
   | https://www.php.net/license/3_01.txt                                 |
9
   | If you did not receive a copy of the PHP license and are unable to   |
10
   | obtain it through the world-wide-web, please send a note to          |
11
   | license@php.net so we can mail you a copy immediately.               |
12
   +----------------------------------------------------------------------+
13
   | Authors: Máté Kocsis <kocsismate@php.net>                            |
14
   +----------------------------------------------------------------------+
15
*/
16
17
#include "php.h"
18
#include "php_lexbor.h"
19
#include "php_uri_common.h"
20
#include "Zend/zend_enum.h"
21
#include "Zend/zend_smart_str.h"
22
#include "Zend/zend_exceptions.h"
23
#ifdef HAVE_ARPA_INET_H
24
#include <arpa/inet.h>
25
#endif
26
27
ZEND_TLS lxb_url_parser_t lexbor_parser;
28
ZEND_TLS unsigned short int lexbor_urls;
29
30
0
#define LEXBOR_MAX_URL_COUNT 500
31
268k
#define LEXBOR_MRAW_BYTE_SIZE 8192
32
33
static zend_always_inline void zval_string_or_null_to_lexbor_str(zval *value, lexbor_str_t *lexbor_str)
34
0
{
35
0
  if (Z_TYPE_P(value) == IS_STRING && Z_STRLEN_P(value) > 0) {
36
0
    lexbor_str->data = (lxb_char_t *) Z_STRVAL_P(value);
37
0
    lexbor_str->length = Z_STRLEN_P(value);
38
0
  } else {
39
0
    ZEND_ASSERT(Z_ISNULL_P(value) || (Z_TYPE_P(value) == IS_STRING && Z_STRLEN_P(value) == 0));
40
0
    lexbor_str->data = (lxb_char_t *) "";
41
0
    lexbor_str->length = 0;
42
0
  }
43
0
}
44
45
static zend_always_inline void zval_long_or_null_to_lexbor_str(zval *value, lexbor_str_t *lexbor_str)
46
0
{
47
0
  if (Z_TYPE_P(value) == IS_LONG) {
48
0
    ZVAL_STR(value, zend_long_to_str(Z_LVAL_P(value)));
49
0
    lexbor_str_init_append(lexbor_str, lexbor_parser.mraw, (const lxb_char_t *) Z_STRVAL_P(value), Z_STRLEN_P(value));
50
0
    zval_ptr_dtor_str(value);
51
0
  } else {
52
0
    ZEND_ASSERT(Z_ISNULL_P(value));
53
0
    lexbor_str->data = (lxb_char_t *) "";
54
0
    lexbor_str->length = 0;
55
0
  }
56
0
}
57
58
static void lexbor_cleanup_parser(void)
59
0
{
60
0
  if (++lexbor_urls % LEXBOR_MAX_URL_COUNT == 0) {
61
0
    lexbor_mraw_clean(lexbor_parser.mraw);
62
0
    lexbor_urls = 0;
63
0
  }
64
65
0
  lxb_url_parser_clean(&lexbor_parser);
66
0
}
67
68
/**
69
 * Creates a Uri\WhatWg\UrlValidationError class by mapping error codes listed in
70
 * https://url.spec.whatwg.org/#writing to a Uri\WhatWg\UrlValidationErrorType enum.
71
 * The result is passed by reference to the errors parameter.
72
 *
73
 * When errors is NULL, the caller is not interested in the additional error information,
74
 * so the function does nothing.
75
 */
76
static const char *fill_errors(zval *errors)
77
0
{
78
0
  if (errors == NULL) {
79
0
    return NULL;
80
0
  }
81
82
0
  ZEND_ASSERT(Z_ISUNDEF_P(errors));
83
84
0
  array_init(errors);
85
86
0
  if (lexbor_parser.log == NULL) {
87
0
    return NULL;
88
0
  }
89
90
0
  const char *result = NULL;
91
0
  lexbor_plog_entry_t *lxb_error;
92
0
  while ((lxb_error = lexbor_array_obj_pop(&lexbor_parser.log->list)) != NULL) {
93
0
    zval error;
94
0
    object_init_ex(&error, uri_whatwg_url_validation_error_ce);
95
0
    zend_update_property_string(uri_whatwg_url_validation_error_ce, Z_OBJ(error), ZEND_STRL("context"), (const char *) lxb_error->data);
96
97
0
    const char *error_str;
98
0
    zval failure;
99
0
    switch (lxb_error->id) {
100
0
      case LXB_URL_ERROR_TYPE_DOMAIN_TO_ASCII:
101
0
        error_str = "DomainToAscii";
102
0
        ZVAL_TRUE(&failure);
103
0
        break;
104
0
      case LXB_URL_ERROR_TYPE_DOMAIN_TO_UNICODE:
105
0
        error_str = "DomainToUnicode";
106
0
        ZVAL_FALSE(&failure);
107
0
        break;
108
0
      case LXB_URL_ERROR_TYPE_DOMAIN_INVALID_CODE_POINT:
109
0
        error_str = "DomainInvalidCodePoint";
110
0
        ZVAL_TRUE(&failure);
111
0
        break;
112
0
      case LXB_URL_ERROR_TYPE_HOST_INVALID_CODE_POINT:
113
0
        error_str = "HostInvalidCodePoint";
114
0
        ZVAL_TRUE(&failure);
115
0
        break;
116
0
      case LXB_URL_ERROR_TYPE_IPV4_EMPTY_PART:
117
0
        error_str = "Ipv4EmptyPart";
118
0
        ZVAL_FALSE(&failure);
119
0
        break;
120
0
      case LXB_URL_ERROR_TYPE_IPV4_TOO_MANY_PARTS:
121
0
        error_str = "Ipv4TooManyParts";
122
0
        ZVAL_TRUE(&failure);
123
0
        break;
124
0
      case LXB_URL_ERROR_TYPE_IPV4_NON_NUMERIC_PART:
125
0
        error_str = "Ipv4NonNumericPart";
126
0
        ZVAL_TRUE(&failure);
127
0
        break;
128
0
      case LXB_URL_ERROR_TYPE_IPV4_NON_DECIMAL_PART:
129
0
        error_str = "Ipv4NonDecimalPart";
130
0
        ZVAL_FALSE(&failure);
131
0
        break;
132
0
      case LXB_URL_ERROR_TYPE_IPV4_OUT_OF_RANGE_PART:
133
0
        error_str = "Ipv4OutOfRangePart";
134
0
        ZVAL_TRUE(&failure);
135
0
        break;
136
0
      case LXB_URL_ERROR_TYPE_IPV6_UNCLOSED:
137
0
        error_str = "Ipv6Unclosed";
138
0
        ZVAL_TRUE(&failure);
139
0
        break;
140
0
      case LXB_URL_ERROR_TYPE_IPV6_INVALID_COMPRESSION:
141
0
        error_str = "Ipv6InvalidCompression";
142
0
        ZVAL_TRUE(&failure);
143
0
        break;
144
0
      case LXB_URL_ERROR_TYPE_IPV6_TOO_MANY_PIECES:
145
0
        error_str = "Ipv6TooManyPieces";
146
0
        ZVAL_TRUE(&failure);
147
0
        break;
148
0
      case LXB_URL_ERROR_TYPE_IPV6_MULTIPLE_COMPRESSION:
149
0
        error_str = "Ipv6MultipleCompression";
150
0
        ZVAL_TRUE(&failure);
151
0
        break;
152
0
      case LXB_URL_ERROR_TYPE_IPV6_INVALID_CODE_POINT:
153
0
        error_str = "Ipv6InvalidCodePoint";
154
0
        ZVAL_TRUE(&failure);
155
0
        break;
156
0
      case LXB_URL_ERROR_TYPE_IPV6_TOO_FEW_PIECES:
157
0
        error_str = "Ipv6TooFewPieces";
158
0
        ZVAL_TRUE(&failure);
159
0
        break;
160
0
      case LXB_URL_ERROR_TYPE_IPV4_IN_IPV6_TOO_MANY_PIECES:
161
0
        error_str = "Ipv4InIpv6TooManyPieces";
162
0
        ZVAL_TRUE(&failure);
163
0
        break;
164
0
      case LXB_URL_ERROR_TYPE_IPV4_IN_IPV6_INVALID_CODE_POINT:
165
0
        error_str = "Ipv4InIpv6InvalidCodePoint";
166
0
        ZVAL_TRUE(&failure);
167
0
        break;
168
0
      case LXB_URL_ERROR_TYPE_IPV4_IN_IPV6_OUT_OF_RANGE_PART:
169
0
        error_str = "Ipv4InIpv6OutOfRangePart";
170
0
        ZVAL_TRUE(&failure);
171
0
        break;
172
0
      case LXB_URL_ERROR_TYPE_IPV4_IN_IPV6_TOO_FEW_PARTS:
173
0
        error_str = "Ipv4InIpv6TooFewParts";
174
0
        ZVAL_TRUE(&failure);
175
0
        break;
176
0
      case LXB_URL_ERROR_TYPE_INVALID_URL_UNIT:
177
0
        error_str = "InvalidUrlUnit";
178
0
        ZVAL_FALSE(&failure);
179
0
        break;
180
0
      case LXB_URL_ERROR_TYPE_SPECIAL_SCHEME_MISSING_FOLLOWING_SOLIDUS:
181
0
        error_str = "SpecialSchemeMissingFollowingSolidus";
182
0
        ZVAL_FALSE(&failure);
183
0
        break;
184
0
      case LXB_URL_ERROR_TYPE_MISSING_SCHEME_NON_RELATIVE_URL:
185
0
        error_str = "MissingSchemeNonRelativeUrl";
186
0
        ZVAL_TRUE(&failure);
187
0
        break;
188
0
      case LXB_URL_ERROR_TYPE_INVALID_REVERSE_SOLIDUS:
189
0
        error_str = "InvalidReverseSoldius";
190
0
        ZVAL_FALSE(&failure);
191
0
        break;
192
0
      case LXB_URL_ERROR_TYPE_INVALID_CREDENTIALS:
193
0
        error_str = "InvalidCredentials";
194
0
        ZVAL_FALSE(&failure);
195
0
        break;
196
0
      case LXB_URL_ERROR_TYPE_HOST_MISSING:
197
0
        error_str = "HostMissing";
198
0
        ZVAL_TRUE(&failure);
199
0
        break;
200
0
      case LXB_URL_ERROR_TYPE_PORT_OUT_OF_RANGE:
201
0
        error_str = "PortOutOfRange";
202
0
        ZVAL_TRUE(&failure);
203
0
        break;
204
0
      case LXB_URL_ERROR_TYPE_PORT_INVALID:
205
0
        error_str = "PortInvalid";
206
0
        ZVAL_TRUE(&failure);
207
0
        break;
208
0
      case LXB_URL_ERROR_TYPE_FILE_INVALID_WINDOWS_DRIVE_LETTER:
209
0
        error_str = "FileInvalidWindowsDriveLetter";
210
0
        ZVAL_FALSE(&failure);
211
0
        break;
212
0
      case LXB_URL_ERROR_TYPE_FILE_INVALID_WINDOWS_DRIVE_LETTER_HOST:
213
0
        error_str = "FileInvalidWindowsDriveLetterHost";
214
0
        ZVAL_FALSE(&failure);
215
0
        break;
216
0
      EMPTY_SWITCH_DEFAULT_CASE()
217
0
    }
218
219
0
    zval error_type;
220
0
    ZVAL_OBJ(&error_type, zend_enum_get_case_cstr(uri_whatwg_url_validation_error_type_ce, error_str));
221
0
    zend_update_property_ex(uri_whatwg_url_validation_error_ce, Z_OBJ(error), ZSTR_KNOWN(ZEND_STR_TYPE), &error_type);
222
223
0
    zend_update_property(uri_whatwg_url_validation_error_ce, Z_OBJ(error), ZEND_STRL("failure"), &failure);
224
225
0
    if (Z_TYPE(failure) == IS_TRUE) {
226
0
      result = error_str;
227
0
    }
228
229
0
    add_next_index_zval(errors, &error);
230
0
  }
231
232
0
  return result;
233
0
}
234
235
static void throw_invalid_url_exception_during_write(zval *errors, const char *component)
236
0
{
237
0
  const char *reason = fill_errors(errors);
238
0
  zend_object *exception = zend_throw_exception_ex(
239
0
    uri_whatwg_invalid_url_exception_ce,
240
0
    0,
241
0
    "The specified %s is malformed%s%s%s",
242
0
    component,
243
0
    reason ? " (" : "",
244
0
    reason ? reason : "",
245
0
    reason ? ")" : ""
246
0
  );
247
0
  zend_update_property(exception->ce, exception, ZEND_STRL("errors"), errors);
248
0
}
249
250
static lxb_status_t lexbor_serialize_callback(const lxb_char_t *data, size_t length, void *ctx)
251
0
{
252
0
  smart_str *uri_str = ctx;
253
254
0
  if (data != NULL && length > 0) {
255
0
    smart_str_appendl(uri_str, (const char *) data, length);
256
0
  }
257
258
0
  return LXB_STATUS_OK;
259
0
}
260
261
static zend_result lexbor_read_scheme(const struct uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval)
262
0
{
263
0
  lxb_url_t *lexbor_uri = internal_uri->uri;
264
265
0
  ZEND_ASSERT(lexbor_uri->scheme.type != LXB_URL_SCHEMEL_TYPE__UNDEF);
266
267
0
  ZVAL_STRINGL(retval, (const char *) lexbor_uri->scheme.name.data, lexbor_uri->scheme.name.length);
268
269
0
  return SUCCESS;
270
0
}
271
272
static zend_result lexbor_write_scheme(struct uri_internal_t *internal_uri, zval *value, zval *errors)
273
0
{
274
0
  lxb_url_t *lexbor_uri = internal_uri->uri;
275
0
  lexbor_str_t str = {0};
276
277
0
  zval_string_or_null_to_lexbor_str(value, &str);
278
279
0
  if (lxb_url_api_protocol_set(lexbor_uri, &lexbor_parser, str.data, str.length) != LXB_STATUS_OK) {
280
0
    throw_invalid_url_exception_during_write(errors, "scheme");
281
282
0
    return FAILURE;
283
0
  }
284
285
0
  return SUCCESS;
286
0
}
287
288
static zend_result lexbor_read_username(const struct uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval)
289
0
{
290
0
  lxb_url_t *lexbor_uri = internal_uri->uri;
291
292
0
  if (lexbor_uri->username.length) {
293
0
    ZVAL_STRINGL(retval, (const char *) lexbor_uri->username.data, lexbor_uri->username.length);
294
0
  } else {
295
0
    ZVAL_NULL(retval);
296
0
  }
297
298
0
  return SUCCESS;
299
0
}
300
301
static zend_result lexbor_write_username(uri_internal_t *internal_uri, zval *value, zval *errors)
302
0
{
303
0
  lxb_url_t *lexbor_uri = internal_uri->uri;
304
0
  lexbor_str_t str = {0};
305
306
0
  zval_string_or_null_to_lexbor_str(value, &str);
307
308
0
  if (lxb_url_api_username_set(lexbor_uri, str.data, str.length) != LXB_STATUS_OK) {
309
0
    throw_invalid_url_exception_during_write(errors, "username");
310
311
0
    return FAILURE;
312
0
  }
313
314
0
  return SUCCESS;
315
0
}
316
317
static zend_result lexbor_read_password(const struct uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval)
318
0
{
319
0
  lxb_url_t *lexbor_uri = internal_uri->uri;
320
321
0
  if (lexbor_uri->password.length > 0) {
322
0
    ZVAL_STRINGL(retval, (const char *) lexbor_uri->password.data, lexbor_uri->password.length);
323
0
  } else {
324
0
    ZVAL_NULL(retval);
325
0
  }
326
327
0
  return SUCCESS;
328
0
}
329
330
static zend_result lexbor_write_password(struct uri_internal_t *internal_uri, zval *value, zval *errors)
331
0
{
332
0
  lxb_url_t *lexbor_uri = internal_uri->uri;
333
0
  lexbor_str_t str = {0};
334
335
0
  zval_string_or_null_to_lexbor_str(value, &str);
336
337
0
  if (lxb_url_api_password_set(lexbor_uri, str.data, str.length) != LXB_STATUS_OK) {
338
0
    throw_invalid_url_exception_during_write(errors, "password");
339
340
0
    return FAILURE;
341
0
  }
342
343
0
  return SUCCESS;
344
0
}
345
346
static zend_result init_idna(void)
347
0
{
348
0
  if (lexbor_parser.idna != NULL) {
349
0
    return SUCCESS;
350
0
  }
351
352
0
  lexbor_parser.idna = lxb_unicode_idna_create();
353
354
0
  return lxb_unicode_idna_init(lexbor_parser.idna) == LXB_STATUS_OK ? SUCCESS : FAILURE;
355
0
}
356
357
static zend_result lexbor_read_host(const struct uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval)
358
0
{
359
0
  lxb_url_t *lexbor_uri = internal_uri->uri;
360
361
0
  if (lexbor_uri->host.type == LXB_URL_HOST_TYPE_IPV4) {
362
0
    smart_str host_str = {0};
363
364
0
    lxb_url_serialize_host_ipv4(lexbor_uri->host.u.ipv4, lexbor_serialize_callback, &host_str);
365
366
0
    ZVAL_NEW_STR(retval, smart_str_extract(&host_str));
367
0
  } else if (lexbor_uri->host.type == LXB_URL_HOST_TYPE_IPV6) {
368
0
    smart_str host_str = {0};
369
370
0
    smart_str_appendc(&host_str, '[');
371
0
    lxb_url_serialize_host_ipv6(lexbor_uri->host.u.ipv6, lexbor_serialize_callback, &host_str);
372
0
    smart_str_appendc(&host_str, ']');
373
374
0
    ZVAL_NEW_STR(retval, smart_str_extract(&host_str));
375
0
  } else if (lexbor_uri->host.type == LXB_URL_HOST_TYPE_EMPTY) {
376
0
    ZVAL_EMPTY_STRING(retval);
377
0
  } else if (lexbor_uri->host.type != LXB_URL_HOST_TYPE__UNDEF) {
378
0
    switch (read_mode) {
379
0
      case URI_COMPONENT_READ_NORMALIZED_UNICODE: {
380
0
        smart_str host_str = {0};
381
0
        if (init_idna() == FAILURE) {
382
0
          return FAILURE;
383
0
        }
384
0
        lxb_url_serialize_host_unicode(lexbor_parser.idna, &lexbor_uri->host, lexbor_serialize_callback, &host_str);
385
0
        lxb_unicode_idna_clean(lexbor_parser.idna);
386
387
0
        ZVAL_NEW_STR(retval, smart_str_extract(&host_str));
388
0
        break;
389
0
      }
390
0
      case URI_COMPONENT_READ_NORMALIZED_ASCII:
391
0
        ZEND_FALLTHROUGH;
392
0
      case URI_COMPONENT_READ_RAW:
393
0
        ZVAL_STRINGL(retval, (const char *) lexbor_uri->host.u.domain.data, lexbor_uri->host.u.domain.length);
394
0
        break;
395
0
      EMPTY_SWITCH_DEFAULT_CASE()
396
0
    }
397
0
  } else {
398
0
    ZVAL_NULL(retval);
399
0
  }
400
401
0
  return SUCCESS;
402
0
}
403
404
static zend_result lexbor_write_host(struct uri_internal_t *internal_uri, zval *value, zval *errors)
405
0
{
406
0
  lxb_url_t *lexbor_uri = internal_uri->uri;
407
0
  lexbor_str_t str = {0};
408
409
0
  zval_string_or_null_to_lexbor_str(value, &str);
410
411
0
  if (lxb_url_api_hostname_set(lexbor_uri, &lexbor_parser, str.data, str.length) != LXB_STATUS_OK) {
412
0
    throw_invalid_url_exception_during_write(errors, "host");
413
414
0
    return FAILURE;
415
0
  }
416
417
0
  return SUCCESS;
418
0
}
419
420
static zend_result lexbor_read_port(const struct uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval)
421
0
{
422
0
  lxb_url_t *lexbor_uri = internal_uri->uri;
423
424
0
  if (lexbor_uri->has_port) {
425
0
    ZVAL_LONG(retval, lexbor_uri->port);
426
0
  } else {
427
0
    ZVAL_NULL(retval);
428
0
  }
429
430
0
  return SUCCESS;
431
0
}
432
433
static zend_result lexbor_write_port(struct uri_internal_t *internal_uri, zval *value, zval *errors)
434
0
{
435
0
  lxb_url_t *lexbor_uri = internal_uri->uri;
436
0
  lexbor_str_t str = {0};
437
438
0
  zval_long_or_null_to_lexbor_str(value, &str);
439
440
0
  if (lxb_url_api_port_set(lexbor_uri, &lexbor_parser, str.data, str.length) != LXB_STATUS_OK) {
441
0
    throw_invalid_url_exception_during_write(errors, "port");
442
443
0
    return FAILURE;
444
0
  }
445
446
0
  return SUCCESS;
447
0
}
448
449
static zend_result lexbor_read_path(const struct uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval)
450
0
{
451
0
  lxb_url_t *lexbor_uri = internal_uri->uri;
452
453
0
  if (lexbor_uri->path.str.length) {
454
0
    ZVAL_STRINGL(retval, (const char *) lexbor_uri->path.str.data, lexbor_uri->path.str.length);
455
0
  } else {
456
0
    ZVAL_EMPTY_STRING(retval);
457
0
  }
458
459
0
  return SUCCESS;
460
0
}
461
462
static zend_result lexbor_write_path(struct uri_internal_t *internal_uri, zval *value, zval *errors)
463
0
{
464
0
  lxb_url_t *lexbor_uri = internal_uri->uri;
465
0
  lexbor_str_t str = {0};
466
467
0
  zval_string_or_null_to_lexbor_str(value, &str);
468
469
0
  if (lxb_url_api_pathname_set(lexbor_uri, &lexbor_parser, str.data, str.length) != LXB_STATUS_OK) {
470
0
    throw_invalid_url_exception_during_write(errors, "path");
471
472
0
    return FAILURE;
473
0
  }
474
475
0
  return SUCCESS;
476
0
}
477
478
static zend_result lexbor_read_query(const struct uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval)
479
0
{
480
0
  lxb_url_t *lexbor_uri = internal_uri->uri;
481
482
0
  if (lexbor_uri->query.length) {
483
0
    ZVAL_STRINGL(retval, (const char *) lexbor_uri->query.data, lexbor_uri->query.length);
484
0
  } else {
485
0
    ZVAL_NULL(retval);
486
0
  }
487
488
0
  return SUCCESS;
489
0
}
490
491
static zend_result lexbor_write_query(struct uri_internal_t *internal_uri, zval *value, zval *errors)
492
0
{
493
0
  lxb_url_t *lexbor_uri = internal_uri->uri;
494
0
  lexbor_str_t str = {0};
495
496
0
  zval_string_or_null_to_lexbor_str(value, &str);
497
498
0
  if (lxb_url_api_search_set(lexbor_uri, &lexbor_parser, str.data, str.length) != LXB_STATUS_OK) {
499
0
    throw_invalid_url_exception_during_write(errors, "query string");
500
501
0
    return FAILURE;
502
0
  }
503
504
0
  return SUCCESS;
505
0
}
506
507
static zend_result lexbor_read_fragment(const struct uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval)
508
0
{
509
0
  lxb_url_t *lexbor_uri = internal_uri->uri;
510
511
0
  if (lexbor_uri->fragment.length) {
512
0
    ZVAL_STRINGL(retval, (const char *) lexbor_uri->fragment.data, lexbor_uri->fragment.length);
513
0
  } else {
514
0
    ZVAL_NULL(retval);
515
0
  }
516
517
0
  return SUCCESS;
518
0
}
519
520
static zend_result lexbor_write_fragment(struct uri_internal_t *internal_uri, zval *value, zval *errors)
521
0
{
522
0
  lxb_url_t *lexbor_uri = internal_uri->uri;
523
0
  lexbor_str_t str = {0};
524
525
0
  zval_string_or_null_to_lexbor_str(value, &str);
526
527
0
  if (lxb_url_api_hash_set(lexbor_uri, &lexbor_parser, str.data, str.length) != LXB_STATUS_OK) {
528
0
    throw_invalid_url_exception_during_write(errors, "fragment");
529
530
0
    return FAILURE;
531
0
  }
532
533
0
  return SUCCESS;
534
0
}
535
536
zend_result lexbor_request_init(void)
537
268k
{
538
268k
  lexbor_mraw_t *mraw = lexbor_mraw_create();
539
268k
  lxb_status_t status = lexbor_mraw_init(mraw, LEXBOR_MRAW_BYTE_SIZE);
540
268k
  if (status != LXB_STATUS_OK) {
541
0
    lexbor_mraw_destroy(mraw, true);
542
0
    return FAILURE;
543
0
  }
544
545
268k
  status = lxb_url_parser_init(&lexbor_parser, mraw);
546
268k
  if (status != LXB_STATUS_OK) {
547
0
    lxb_url_parser_destroy(&lexbor_parser, false);
548
0
    lexbor_mraw_destroy(mraw, true);
549
0
    return FAILURE;
550
0
  }
551
552
268k
  lexbor_urls = 0;
553
554
268k
  return SUCCESS;
555
268k
}
556
557
void lexbor_request_shutdown(void)
558
268k
{
559
268k
  lxb_url_parser_memory_destroy(&lexbor_parser);
560
268k
  lxb_url_parser_destroy(&lexbor_parser, false);
561
562
268k
  lexbor_urls = 0;
563
268k
}
564
565
lxb_url_t *lexbor_parse_uri_ex(const zend_string *uri_str, const lxb_url_t *lexbor_base_url, zval *errors, bool silent)
566
0
{
567
0
  lexbor_cleanup_parser();
568
569
0
  lxb_url_t *url = lxb_url_parse(&lexbor_parser, lexbor_base_url, (unsigned char *) ZSTR_VAL(uri_str), ZSTR_LEN(uri_str));
570
0
  const char *reason = fill_errors(errors);
571
572
0
  if (url == NULL && !silent) {
573
0
    zend_object *exception = zend_throw_exception_ex(uri_whatwg_invalid_url_exception_ce, 0, "The specified URI is malformed%s%s%s", reason ? " (" : "", reason ? reason : "", reason ? ")" : "");
574
0
    zend_update_property(exception->ce, exception, ZEND_STRL("errors"), errors);
575
0
  }
576
577
0
  return url;
578
0
}
579
580
static void *lexbor_parse_uri(const zend_string *uri_str, const void *base_url, zval *errors, bool silent)
581
0
{
582
0
  return lexbor_parse_uri_ex(uri_str, base_url, errors, silent);
583
0
}
584
585
static void *lexbor_clone_uri(void *uri)
586
0
{
587
0
  lxb_url_t *lexbor_uri = (lxb_url_t *) uri;
588
589
0
  return lxb_url_clone(lexbor_parser.mraw, lexbor_uri);
590
0
}
591
592
static zend_string *lexbor_uri_to_string(void *uri, uri_recomposition_mode_t recomposition_mode, bool exclude_fragment)
593
0
{
594
0
  lxb_url_t *lexbor_uri = (lxb_url_t *) uri;
595
0
  smart_str uri_str = {0};
596
597
0
  switch (recomposition_mode) {
598
0
    case URI_RECOMPOSITION_RAW_UNICODE:
599
0
      ZEND_FALLTHROUGH;
600
0
    case URI_RECOMPOSITION_NORMALIZED_UNICODE:
601
0
      if (init_idna() == FAILURE) {
602
0
        return NULL;
603
0
      }
604
0
      lxb_url_serialize_idna(lexbor_parser.idna, lexbor_uri, lexbor_serialize_callback, &uri_str, exclude_fragment);
605
0
      lxb_unicode_idna_clean(lexbor_parser.idna);
606
0
      break;
607
0
    case URI_RECOMPOSITION_RAW_ASCII:
608
0
      ZEND_FALLTHROUGH;
609
0
    case URI_RECOMPOSITION_NORMALIZED_ASCII:
610
0
      lxb_url_serialize(lexbor_uri, lexbor_serialize_callback, &uri_str, exclude_fragment);
611
0
      break;
612
0
    EMPTY_SWITCH_DEFAULT_CASE()
613
0
  }
614
615
0
  return smart_str_extract(&uri_str);
616
0
}
617
618
static void lexbor_free_uri(void *uri)
619
0
{
620
0
}
621
622
const uri_handler_t lexbor_uri_handler = {
623
  .name = URI_PARSER_WHATWG,
624
  .parse_uri = lexbor_parse_uri,
625
  .clone_uri = lexbor_clone_uri,
626
  .uri_to_string = lexbor_uri_to_string,
627
  .free_uri = lexbor_free_uri,
628
  {
629
    .scheme = {.read_func = lexbor_read_scheme, .write_func = lexbor_write_scheme},
630
    .username = {.read_func = lexbor_read_username, .write_func = lexbor_write_username},
631
    .password = {.read_func = lexbor_read_password, .write_func = lexbor_write_password},
632
    .host = {.read_func = lexbor_read_host, .write_func = lexbor_write_host},
633
    .port = {.read_func = lexbor_read_port, .write_func = lexbor_write_port},
634
    .path = {.read_func = lexbor_read_path, .write_func = lexbor_write_path},
635
    .query = {.read_func = lexbor_read_query, .write_func = lexbor_write_query},
636
    .fragment = {.read_func = lexbor_read_fragment, .write_func = lexbor_write_fragment},
637
  }
638
};