Coverage Report

Created: 2025-06-13 06:43

/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
300k
#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 void fill_errors(zval *errors)
77
0
{
78
0
  if (errors == NULL) {
79
0
    return;
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;
88
0
  }
89
90
0
  lexbor_plog_entry_t *lxb_error;
91
0
  while ((lxb_error = lexbor_array_obj_pop(&lexbor_parser.log->list)) != NULL) {
92
0
    zval error;
93
0
    object_init_ex(&error, uri_whatwg_url_validation_error_ce);
94
0
    zend_update_property_string(uri_whatwg_url_validation_error_ce, Z_OBJ(error), ZEND_STRL("context"), (const char *) lxb_error->data);
95
96
0
    zend_string *error_str;
97
0
    zval failure;
98
0
    switch (lxb_error->id) {
99
0
      case LXB_URL_ERROR_TYPE_DOMAIN_TO_ASCII:
100
0
        error_str = ZSTR_INIT_LITERAL("DomainToAscii", false);
101
0
        ZVAL_TRUE(&failure);
102
0
        break;
103
0
      case LXB_URL_ERROR_TYPE_DOMAIN_TO_UNICODE:
104
0
        error_str = ZSTR_INIT_LITERAL("DomainToUnicode", false);
105
0
        ZVAL_FALSE(&failure);
106
0
        break;
107
0
      case LXB_URL_ERROR_TYPE_DOMAIN_INVALID_CODE_POINT:
108
0
        error_str = ZSTR_INIT_LITERAL("DomainInvalidCodePoint", false);
109
0
        ZVAL_TRUE(&failure);
110
0
        break;
111
0
      case LXB_URL_ERROR_TYPE_HOST_INVALID_CODE_POINT:
112
0
        error_str = ZSTR_INIT_LITERAL("HostInvalidCodePoint", false);
113
0
        ZVAL_TRUE(&failure);
114
0
        break;
115
0
      case LXB_URL_ERROR_TYPE_IPV4_EMPTY_PART:
116
0
        error_str = ZSTR_INIT_LITERAL("Ipv4EmptyPart", false);
117
0
        ZVAL_FALSE(&failure);
118
0
        break;
119
0
      case LXB_URL_ERROR_TYPE_IPV4_TOO_MANY_PARTS:
120
0
        error_str = ZSTR_INIT_LITERAL("Ipv4TooManyParts", false);
121
0
        ZVAL_TRUE(&failure);
122
0
        break;
123
0
      case LXB_URL_ERROR_TYPE_IPV4_NON_NUMERIC_PART:
124
0
        error_str = ZSTR_INIT_LITERAL("Ipv4NonNumericPart", false);
125
0
        ZVAL_TRUE(&failure);
126
0
        break;
127
0
      case LXB_URL_ERROR_TYPE_IPV4_NON_DECIMAL_PART:
128
0
        error_str = ZSTR_INIT_LITERAL("Ipv4NonDecimalPart", false);
129
0
        ZVAL_FALSE(&failure);
130
0
        break;
131
0
      case LXB_URL_ERROR_TYPE_IPV4_OUT_OF_RANGE_PART:
132
0
        error_str = ZSTR_INIT_LITERAL("Ipv4OutOfRangePart", false);
133
0
        ZVAL_TRUE(&failure);
134
0
        break;
135
0
      case LXB_URL_ERROR_TYPE_IPV6_UNCLOSED:
136
0
        error_str = ZSTR_INIT_LITERAL("Ipv6Unclosed", false);
137
0
        ZVAL_TRUE(&failure);
138
0
        break;
139
0
      case LXB_URL_ERROR_TYPE_IPV6_INVALID_COMPRESSION:
140
0
        error_str = ZSTR_INIT_LITERAL("Ipv6InvalidCompression", false);
141
0
        ZVAL_TRUE(&failure);
142
0
        break;
143
0
      case LXB_URL_ERROR_TYPE_IPV6_TOO_MANY_PIECES:
144
0
        error_str = ZSTR_INIT_LITERAL("Ipv6TooManyPieces", false);
145
0
        ZVAL_TRUE(&failure);
146
0
        break;
147
0
      case LXB_URL_ERROR_TYPE_IPV6_MULTIPLE_COMPRESSION:
148
0
        error_str = ZSTR_INIT_LITERAL("Ipv6MultipleCompression", false);
149
0
        ZVAL_TRUE(&failure);
150
0
        break;
151
0
      case LXB_URL_ERROR_TYPE_IPV6_INVALID_CODE_POINT:
152
0
        error_str = ZSTR_INIT_LITERAL("Ipv6InvalidCodePoint", false);
153
0
        ZVAL_TRUE(&failure);
154
0
        break;
155
0
      case LXB_URL_ERROR_TYPE_IPV6_TOO_FEW_PIECES:
156
0
        error_str = ZSTR_INIT_LITERAL("Ipv6TooFewPieces", false);
157
0
        ZVAL_TRUE(&failure);
158
0
        break;
159
0
      case LXB_URL_ERROR_TYPE_IPV4_IN_IPV6_TOO_MANY_PIECES:
160
0
        error_str = ZSTR_INIT_LITERAL("Ipv4InIpv6TooManyPieces", false);
161
0
        ZVAL_TRUE(&failure);
162
0
        break;
163
0
      case LXB_URL_ERROR_TYPE_IPV4_IN_IPV6_INVALID_CODE_POINT:
164
0
        error_str = ZSTR_INIT_LITERAL("Ipv4InIpv6InvalidCodePoint", false);
165
0
        ZVAL_TRUE(&failure);
166
0
        break;
167
0
      case LXB_URL_ERROR_TYPE_IPV4_IN_IPV6_OUT_OF_RANGE_PART:
168
0
        error_str = ZSTR_INIT_LITERAL("Ipv4InIpv6OutOfRangePart", false);
169
0
        ZVAL_TRUE(&failure);
170
0
        break;
171
0
      case LXB_URL_ERROR_TYPE_IPV4_IN_IPV6_TOO_FEW_PARTS:
172
0
        error_str = ZSTR_INIT_LITERAL("Ipv4InIpv6TooFewParts", false);
173
0
        ZVAL_TRUE(&failure);
174
0
        break;
175
0
      case LXB_URL_ERROR_TYPE_INVALID_URL_UNIT:
176
0
        error_str = ZSTR_INIT_LITERAL("InvalidUrlUnit", false);
177
0
        ZVAL_FALSE(&failure);
178
0
        break;
179
0
      case LXB_URL_ERROR_TYPE_SPECIAL_SCHEME_MISSING_FOLLOWING_SOLIDUS:
180
0
        error_str = ZSTR_INIT_LITERAL("SpecialSchemeMissingFollowingSolidus", false);
181
0
        ZVAL_FALSE(&failure);
182
0
        break;
183
0
      case LXB_URL_ERROR_TYPE_MISSING_SCHEME_NON_RELATIVE_URL:
184
0
        error_str = ZSTR_INIT_LITERAL("MissingSchemeNonRelativeUrl", false);
185
0
        ZVAL_TRUE(&failure);
186
0
        break;
187
0
      case LXB_URL_ERROR_TYPE_INVALID_REVERSE_SOLIDUS:
188
0
        error_str = ZSTR_INIT_LITERAL("InvalidReverseSoldius", false);
189
0
        ZVAL_FALSE(&failure);
190
0
        break;
191
0
      case LXB_URL_ERROR_TYPE_INVALID_CREDENTIALS:
192
0
        error_str = ZSTR_INIT_LITERAL("InvalidCredentials", false);
193
0
        ZVAL_FALSE(&failure);
194
0
        break;
195
0
      case LXB_URL_ERROR_TYPE_HOST_MISSING:
196
0
        error_str = ZSTR_INIT_LITERAL("HostMissing", false);
197
0
        ZVAL_TRUE(&failure);
198
0
        break;
199
0
      case LXB_URL_ERROR_TYPE_PORT_OUT_OF_RANGE:
200
0
        error_str = ZSTR_INIT_LITERAL("PortOutOfRange", false);
201
0
        ZVAL_TRUE(&failure);
202
0
        break;
203
0
      case LXB_URL_ERROR_TYPE_PORT_INVALID:
204
0
        error_str = ZSTR_INIT_LITERAL("PortInvalid", false);
205
0
        ZVAL_TRUE(&failure);
206
0
        break;
207
0
      case LXB_URL_ERROR_TYPE_FILE_INVALID_WINDOWS_DRIVE_LETTER:
208
0
        error_str = ZSTR_INIT_LITERAL("FileInvalidWindowsDriveLetter", false);
209
0
        ZVAL_FALSE(&failure);
210
0
        break;
211
0
      case LXB_URL_ERROR_TYPE_FILE_INVALID_WINDOWS_DRIVE_LETTER_HOST:
212
0
        error_str = ZSTR_INIT_LITERAL("FileInvalidWindowsDriveLetterHost", false);
213
0
        ZVAL_FALSE(&failure);
214
0
        break;
215
0
      EMPTY_SWITCH_DEFAULT_CASE()
216
0
    }
217
218
0
    zval error_type;
219
0
    zend_enum_new(&error_type, uri_whatwg_url_validation_error_type_ce, error_str, NULL);
220
0
    zend_update_property_ex(uri_whatwg_url_validation_error_ce, Z_OBJ(error), ZSTR_KNOWN(ZEND_STR_TYPE), &error_type);
221
0
    zend_string_release_ex(error_str, false);
222
0
    zval_ptr_dtor(&error_type);
223
224
0
    zend_update_property(uri_whatwg_url_validation_error_ce, Z_OBJ(error), ZEND_STRL("failure"), &failure);
225
226
0
    add_next_index_zval(errors, &error);
227
0
  }
228
0
}
229
230
static void throw_invalid_url_exception(zval *errors)
231
0
{
232
0
  ZEND_ASSERT(errors != NULL && Z_TYPE_P(errors) == IS_ARRAY);
233
234
0
  zval exception;
235
236
0
  object_init_ex(&exception, uri_whatwg_invalid_url_exception_ce);
237
238
0
  zval value;
239
0
  ZVAL_STRING(&value, "URL parsing failed");
240
0
  zend_update_property_ex(uri_whatwg_invalid_url_exception_ce, Z_OBJ(exception), ZSTR_KNOWN(ZEND_STR_MESSAGE), &value);
241
0
  zval_ptr_dtor_str(&value);
242
243
0
  zend_update_property(uri_whatwg_invalid_url_exception_ce, Z_OBJ(exception), ZEND_STRL("errors"), errors);
244
245
0
  zend_throw_exception_object(&exception);
246
0
}
247
248
static void throw_invalid_url_exception_during_write(zval *errors)
249
0
{
250
0
  fill_errors(errors);
251
0
  throw_invalid_url_exception(errors);
252
0
}
253
254
static lxb_status_t lexbor_serialize_callback(const lxb_char_t *data, size_t length, void *ctx)
255
0
{
256
0
  smart_str *uri_str = ctx;
257
258
0
  if (data != NULL && length > 0) {
259
0
    smart_str_appendl(uri_str, (const char *) data, length);
260
0
  }
261
262
0
  return LXB_STATUS_OK;
263
0
}
264
265
static zend_result lexbor_read_scheme(const struct uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval)
266
0
{
267
0
  lxb_url_t *lexbor_uri = internal_uri->uri;
268
269
0
  ZEND_ASSERT(lexbor_uri->scheme.type != LXB_URL_SCHEMEL_TYPE__UNDEF);
270
271
0
  ZVAL_STRINGL(retval, (const char *) lexbor_uri->scheme.name.data, lexbor_uri->scheme.name.length);
272
273
0
  return SUCCESS;
274
0
}
275
276
static zend_result lexbor_write_scheme(struct uri_internal_t *internal_uri, zval *value, zval *errors)
277
0
{
278
0
  lxb_url_t *lexbor_uri = internal_uri->uri;
279
0
  lexbor_str_t str = {0};
280
281
0
  zval_string_or_null_to_lexbor_str(value, &str);
282
283
0
  if (lxb_url_api_protocol_set(lexbor_uri, &lexbor_parser, str.data, str.length) != LXB_STATUS_OK) {
284
0
    throw_invalid_url_exception_during_write(errors);
285
286
0
    return FAILURE;
287
0
  }
288
289
0
  return SUCCESS;
290
0
}
291
292
static zend_result lexbor_read_username(const struct uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval)
293
0
{
294
0
  lxb_url_t *lexbor_uri = internal_uri->uri;
295
296
0
  if (lexbor_uri->username.length) {
297
0
    ZVAL_STRINGL(retval, (const char *) lexbor_uri->username.data, lexbor_uri->username.length);
298
0
  } else {
299
0
    ZVAL_NULL(retval);
300
0
  }
301
302
0
  return SUCCESS;
303
0
}
304
305
static zend_result lexbor_write_username(uri_internal_t *internal_uri, zval *value, zval *errors)
306
0
{
307
0
  lxb_url_t *lexbor_uri = internal_uri->uri;
308
0
  lexbor_str_t str = {0};
309
310
0
  zval_string_or_null_to_lexbor_str(value, &str);
311
312
0
  if (lxb_url_api_username_set(lexbor_uri, str.data, str.length) != LXB_STATUS_OK) {
313
0
    throw_invalid_url_exception_during_write(errors);
314
315
0
    return FAILURE;
316
0
  }
317
318
0
  return SUCCESS;
319
0
}
320
321
static zend_result lexbor_read_password(const struct uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval)
322
0
{
323
0
  lxb_url_t *lexbor_uri = internal_uri->uri;
324
325
0
  if (lexbor_uri->password.length > 0) {
326
0
    ZVAL_STRINGL(retval, (const char *) lexbor_uri->password.data, lexbor_uri->password.length);
327
0
  } else {
328
0
    ZVAL_NULL(retval);
329
0
  }
330
331
0
  return SUCCESS;
332
0
}
333
334
static zend_result lexbor_write_password(struct uri_internal_t *internal_uri, zval *value, zval *errors)
335
0
{
336
0
  lxb_url_t *lexbor_uri = internal_uri->uri;
337
0
  lexbor_str_t str = {0};
338
339
0
  zval_string_or_null_to_lexbor_str(value, &str);
340
341
0
  if (lxb_url_api_password_set(lexbor_uri, str.data, str.length) != LXB_STATUS_OK) {
342
0
    throw_invalid_url_exception_during_write(errors);
343
344
0
    return FAILURE;
345
0
  }
346
347
0
  return SUCCESS;
348
0
}
349
350
static zend_result init_idna(void)
351
0
{
352
0
  if (lexbor_parser.idna != NULL) {
353
0
    return SUCCESS;
354
0
  }
355
356
0
  lexbor_parser.idna = lxb_unicode_idna_create();
357
358
0
  return lxb_unicode_idna_init(lexbor_parser.idna) == LXB_STATUS_OK ? SUCCESS : FAILURE;
359
0
}
360
361
static zend_result lexbor_read_host(const struct uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval)
362
0
{
363
0
  lxb_url_t *lexbor_uri = internal_uri->uri;
364
365
0
  if (lexbor_uri->host.type == LXB_URL_HOST_TYPE_IPV4) {
366
0
    smart_str host_str = {0};
367
368
0
    lxb_url_serialize_host_ipv4(lexbor_uri->host.u.ipv4, lexbor_serialize_callback, &host_str);
369
370
0
    ZVAL_NEW_STR(retval, smart_str_extract(&host_str));
371
0
  } else if (lexbor_uri->host.type == LXB_URL_HOST_TYPE_IPV6) {
372
0
    smart_str host_str = {0};
373
374
0
    smart_str_appendc(&host_str, '[');
375
0
    lxb_url_serialize_host_ipv6(lexbor_uri->host.u.ipv6, lexbor_serialize_callback, &host_str);
376
0
    smart_str_appendc(&host_str, ']');
377
378
0
    ZVAL_NEW_STR(retval, smart_str_extract(&host_str));
379
0
  } else if (lexbor_uri->host.type != LXB_URL_HOST_TYPE_EMPTY && lexbor_uri->host.type != LXB_URL_HOST_TYPE__UNDEF) {
380
0
    switch (read_mode) {
381
0
      case URI_COMPONENT_READ_NORMALIZED_UNICODE: {
382
0
        smart_str host_str = {0};
383
0
        if (init_idna() == FAILURE) {
384
0
          return FAILURE;
385
0
        }
386
0
        lxb_url_serialize_host_unicode(lexbor_parser.idna, &lexbor_uri->host, lexbor_serialize_callback, &host_str);
387
0
        lxb_unicode_idna_clean(lexbor_parser.idna);
388
389
0
        ZVAL_NEW_STR(retval, smart_str_extract(&host_str));
390
0
        break;
391
0
      }
392
0
      case URI_COMPONENT_READ_NORMALIZED_ASCII:
393
0
        ZEND_FALLTHROUGH;
394
0
      case URI_COMPONENT_READ_RAW:
395
0
        ZVAL_STRINGL(retval, (const char *) lexbor_uri->host.u.domain.data, lexbor_uri->host.u.domain.length);
396
0
        break;
397
0
      EMPTY_SWITCH_DEFAULT_CASE()
398
0
    }
399
0
  } else {
400
0
    ZVAL_NULL(retval);
401
0
  }
402
403
0
  return SUCCESS;
404
0
}
405
406
static zend_result lexbor_write_host(struct uri_internal_t *internal_uri, zval *value, zval *errors)
407
0
{
408
0
  lxb_url_t *lexbor_uri = internal_uri->uri;
409
0
  lexbor_str_t str = {0};
410
411
0
  zval_string_or_null_to_lexbor_str(value, &str);
412
413
0
  if (lxb_url_api_hostname_set(lexbor_uri, &lexbor_parser, str.data, str.length) != LXB_STATUS_OK) {
414
0
    throw_invalid_url_exception_during_write(errors);
415
416
0
    return FAILURE;
417
0
  }
418
419
0
  return SUCCESS;
420
0
}
421
422
static zend_result lexbor_read_port(const struct uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval)
423
0
{
424
0
  lxb_url_t *lexbor_uri = internal_uri->uri;
425
426
0
  if (lexbor_uri->has_port) {
427
0
    ZVAL_LONG(retval, lexbor_uri->port);
428
0
  } else {
429
0
    ZVAL_NULL(retval);
430
0
  }
431
432
0
  return SUCCESS;
433
0
}
434
435
static zend_result lexbor_write_port(struct uri_internal_t *internal_uri, zval *value, zval *errors)
436
0
{
437
0
  lxb_url_t *lexbor_uri = internal_uri->uri;
438
0
  lexbor_str_t str = {0};
439
440
0
  zval_long_or_null_to_lexbor_str(value, &str);
441
442
0
  if (lxb_url_api_port_set(lexbor_uri, &lexbor_parser, str.data, str.length) != LXB_STATUS_OK) {
443
0
    throw_invalid_url_exception_during_write(errors);
444
445
0
    return FAILURE;
446
0
  }
447
448
0
  return SUCCESS;
449
0
}
450
451
static zend_result lexbor_read_path(const struct uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval)
452
0
{
453
0
  lxb_url_t *lexbor_uri = internal_uri->uri;
454
455
0
  if (lexbor_uri->path.str.length) {
456
0
    ZVAL_STRINGL(retval, (const char *) lexbor_uri->path.str.data, lexbor_uri->path.str.length);
457
0
  } else {
458
0
    ZVAL_EMPTY_STRING(retval);
459
0
  }
460
461
0
  return SUCCESS;
462
0
}
463
464
static zend_result lexbor_write_path(struct uri_internal_t *internal_uri, zval *value, zval *errors)
465
0
{
466
0
  lxb_url_t *lexbor_uri = internal_uri->uri;
467
0
  lexbor_str_t str = {0};
468
469
0
  zval_string_or_null_to_lexbor_str(value, &str);
470
471
0
  if (lxb_url_api_pathname_set(lexbor_uri, &lexbor_parser, str.data, str.length) != LXB_STATUS_OK) {
472
0
    throw_invalid_url_exception_during_write(errors);
473
474
0
    return FAILURE;
475
0
  }
476
477
0
  return SUCCESS;
478
0
}
479
480
static zend_result lexbor_read_query(const struct uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval)
481
0
{
482
0
  lxb_url_t *lexbor_uri = internal_uri->uri;
483
484
0
  if (lexbor_uri->query.length) {
485
0
    ZVAL_STRINGL(retval, (const char *) lexbor_uri->query.data, lexbor_uri->query.length);
486
0
  } else {
487
0
    ZVAL_NULL(retval);
488
0
  }
489
490
0
  return SUCCESS;
491
0
}
492
493
static zend_result lexbor_write_query(struct uri_internal_t *internal_uri, zval *value, zval *errors)
494
0
{
495
0
  lxb_url_t *lexbor_uri = internal_uri->uri;
496
0
  lexbor_str_t str = {0};
497
498
0
  zval_string_or_null_to_lexbor_str(value, &str);
499
500
0
  if (lxb_url_api_search_set(lexbor_uri, &lexbor_parser, str.data, str.length) != LXB_STATUS_OK) {
501
0
    throw_invalid_url_exception_during_write(errors);
502
503
0
    return FAILURE;
504
0
  }
505
506
0
  return SUCCESS;
507
0
}
508
509
static zend_result lexbor_read_fragment(const struct uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval)
510
0
{
511
0
  lxb_url_t *lexbor_uri = internal_uri->uri;
512
513
0
  if (lexbor_uri->fragment.length) {
514
0
    ZVAL_STRINGL(retval, (const char *) lexbor_uri->fragment.data, lexbor_uri->fragment.length);
515
0
  } else {
516
0
    ZVAL_NULL(retval);
517
0
  }
518
519
0
  return SUCCESS;
520
0
}
521
522
static zend_result lexbor_write_fragment(struct uri_internal_t *internal_uri, zval *value, zval *errors)
523
0
{
524
0
  lxb_url_t *lexbor_uri = internal_uri->uri;
525
0
  lexbor_str_t str = {0};
526
527
0
  zval_string_or_null_to_lexbor_str(value, &str);
528
529
0
  if (lxb_url_api_hash_set(lexbor_uri, &lexbor_parser, str.data, str.length) != LXB_STATUS_OK) {
530
0
    throw_invalid_url_exception_during_write(errors);
531
532
0
    return FAILURE;
533
0
  }
534
535
0
  return SUCCESS;
536
0
}
537
538
zend_result lexbor_request_init(void)
539
300k
{
540
300k
  lexbor_mraw_t *mraw = lexbor_mraw_create();
541
300k
  lxb_status_t status = lexbor_mraw_init(mraw, LEXBOR_MRAW_BYTE_SIZE);
542
300k
  if (status != LXB_STATUS_OK) {
543
0
    lexbor_mraw_destroy(mraw, true);
544
0
    return FAILURE;
545
0
  }
546
547
300k
  status = lxb_url_parser_init(&lexbor_parser, mraw);
548
300k
  if (status != LXB_STATUS_OK) {
549
0
    lxb_url_parser_destroy(&lexbor_parser, false);
550
0
    lexbor_mraw_destroy(mraw, true);
551
0
    return FAILURE;
552
0
  }
553
554
300k
  lexbor_urls = 0;
555
556
300k
  return SUCCESS;
557
300k
}
558
559
void lexbor_request_shutdown(void)
560
300k
{
561
300k
  lxb_url_parser_memory_destroy(&lexbor_parser);
562
300k
  lxb_url_parser_destroy(&lexbor_parser, false);
563
564
300k
  lexbor_urls = 0;
565
300k
}
566
567
lxb_url_t *lexbor_parse_uri_ex(const zend_string *uri_str, const lxb_url_t *lexbor_base_url, zval *errors, bool silent)
568
0
{
569
0
  lexbor_cleanup_parser();
570
571
0
  lxb_url_t *url = lxb_url_parse(&lexbor_parser, lexbor_base_url, (unsigned char *) ZSTR_VAL(uri_str), ZSTR_LEN(uri_str));
572
0
  fill_errors(errors);
573
574
0
  if (url == NULL && !silent) {
575
0
    throw_invalid_url_exception(errors);
576
0
  }
577
578
0
  return url;
579
0
}
580
581
static void *lexbor_parse_uri(const zend_string *uri_str, const void *base_url, zval *errors, bool silent)
582
0
{
583
0
  return lexbor_parse_uri_ex(uri_str, base_url, errors, silent);
584
0
}
585
586
static void *lexbor_clone_uri(void *uri)
587
0
{
588
0
  lxb_url_t *lexbor_uri = (lxb_url_t *) uri;
589
590
0
  return lxb_url_clone(lexbor_parser.mraw, lexbor_uri);
591
0
}
592
593
static zend_string *lexbor_uri_to_string(void *uri, uri_recomposition_mode_t recomposition_mode, bool exclude_fragment)
594
0
{
595
0
  lxb_url_t *lexbor_uri = (lxb_url_t *) uri;
596
0
  smart_str uri_str = {0};
597
598
0
  switch (recomposition_mode) {
599
0
    case URI_RECOMPOSITION_RAW_UNICODE:
600
0
      ZEND_FALLTHROUGH;
601
0
    case URI_RECOMPOSITION_NORMALIZED_UNICODE:
602
0
      if (init_idna() == FAILURE) {
603
0
        return NULL;
604
0
      }
605
0
      lxb_url_serialize_idna(lexbor_parser.idna, lexbor_uri, lexbor_serialize_callback, &uri_str, exclude_fragment);
606
0
      lxb_unicode_idna_clean(lexbor_parser.idna);
607
0
      break;
608
0
    case URI_RECOMPOSITION_RAW_ASCII:
609
0
      ZEND_FALLTHROUGH;
610
0
    case URI_RECOMPOSITION_NORMALIZED_ASCII:
611
0
      lxb_url_serialize(lexbor_uri, lexbor_serialize_callback, &uri_str, exclude_fragment);
612
0
      break;
613
0
    EMPTY_SWITCH_DEFAULT_CASE()
614
0
  }
615
616
0
  return smart_str_extract(&uri_str);
617
0
}
618
619
static void lexbor_free_uri(void *uri)
620
0
{
621
0
}
622
623
const uri_handler_t lexbor_uri_handler = {
624
  .name = URI_PARSER_WHATWG,
625
  .parse_uri = lexbor_parse_uri,
626
  .clone_uri = lexbor_clone_uri,
627
  .uri_to_string = lexbor_uri_to_string,
628
  .free_uri = lexbor_free_uri,
629
  {
630
    .scheme = {.read_func = lexbor_read_scheme, .write_func = lexbor_write_scheme},
631
    .username = {.read_func = lexbor_read_username, .write_func = lexbor_write_username},
632
    .password = {.read_func = lexbor_read_password, .write_func = lexbor_write_password},
633
    .host = {.read_func = lexbor_read_host, .write_func = lexbor_write_host},
634
    .port = {.read_func = lexbor_read_port, .write_func = lexbor_write_port},
635
    .path = {.read_func = lexbor_read_path, .write_func = lexbor_write_path},
636
    .query = {.read_func = lexbor_read_query, .write_func = lexbor_write_query},
637
    .fragment = {.read_func = lexbor_read_fragment, .write_func = lexbor_write_fragment},
638
  }
639
};