mirror of
https://github.com/lexborisov/Modest
synced 2024-11-28 16:33:08 +03:00
650 lines
16 KiB
C
650 lines
16 KiB
C
/*
|
|
Copyright (C) 2016-2017 Alexander Borisov
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2.1 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with this library; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
Author: lex.borisov@gmail.com (Alexander Borisov)
|
|
*/
|
|
|
|
#include "myurl/url.h"
|
|
#include "myurl/resources.h"
|
|
|
|
myurl_t * myurl_create(void)
|
|
{
|
|
return mycore_calloc(1, sizeof(myurl_t));
|
|
}
|
|
|
|
mystatus_t myurl_init(myurl_t* url)
|
|
{
|
|
url->callback_malloc = myurl_callback_malloc;
|
|
url->callback_free = myurl_callback_free;
|
|
url->callback_realloc = myurl_callback_realloc;
|
|
url->callback_ctx = url;
|
|
|
|
return MyURL_STATUS_OK;
|
|
}
|
|
|
|
void myurl_clean(myurl_t* url)
|
|
{
|
|
memset(url, 0, sizeof(myurl_t));
|
|
}
|
|
|
|
myurl_t * myurl_destroy(myurl_t* url, bool self_destroy)
|
|
{
|
|
if(url == NULL)
|
|
return NULL;
|
|
|
|
if(self_destroy) {
|
|
mycore_free(url);
|
|
return NULL;
|
|
}
|
|
|
|
return url;
|
|
}
|
|
|
|
/*
|
|
* Entry
|
|
*/
|
|
myurl_entry_t * myurl_entry_create_and_init(myurl_t* url)
|
|
{
|
|
myurl_entry_t *entry = url->callback_malloc(sizeof(myurl_entry_t), url->callback_ctx);
|
|
if(entry == NULL)
|
|
return NULL;
|
|
|
|
memset(entry, 0, sizeof(myurl_entry_t));
|
|
|
|
if(myurl_path_init(url, &entry->path, 56) != MyURL_STATUS_OK) {
|
|
return url->callback_free(entry, url->callback_ctx);
|
|
}
|
|
|
|
entry->url_ref = url;
|
|
|
|
return entry;
|
|
}
|
|
|
|
void myurl_entry_clean(myurl_entry_t* url_entry)
|
|
{
|
|
myurl_t* url = url_entry->url_ref;
|
|
|
|
if(url_entry->username)
|
|
url->callback_free(url_entry->username, url->callback_ctx);
|
|
|
|
if(url_entry->password)
|
|
url->callback_free(url_entry->password, url->callback_ctx);
|
|
|
|
if(url_entry->query)
|
|
url->callback_free(url_entry->query, url->callback_ctx);
|
|
|
|
if(url_entry->fragment)
|
|
url->callback_free(url_entry->fragment, url->callback_ctx);
|
|
|
|
myurl_host_destroy(url, &url_entry->host, false);
|
|
myurl_path_destroy(url, &url_entry->path, false);
|
|
myurl_scheme_destroy(url, &url_entry->scheme, false);
|
|
|
|
memset(url_entry, 0, sizeof(myurl_entry_t));
|
|
|
|
url_entry->url_ref = url;
|
|
}
|
|
|
|
myurl_entry_t * myurl_entry_destroy(myurl_entry_t* url_entry, bool self_destroy)
|
|
{
|
|
if(url_entry == NULL)
|
|
return NULL;
|
|
|
|
myurl_entry_clean(url_entry);
|
|
|
|
if(self_destroy) {
|
|
return url_entry->url_ref->callback_free(url_entry, url_entry->url_ref->callback_ctx);
|
|
}
|
|
|
|
return url_entry;
|
|
}
|
|
|
|
/*
|
|
* The CODE
|
|
*/
|
|
myurl_entry_t * myurl_parse(myurl_t* url, const char* data_url, size_t data_url_size, myurl_entry_t* base_url, mystatus_t* status)
|
|
{
|
|
myurl_entry_t* entry = myurl_entry_create_and_init(url);
|
|
|
|
if(entry == NULL) {
|
|
if(status)
|
|
*status = MyURL_STATUS_ERROR_MEMORY_ALLOCATION;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
myurl_parser_begin(url, entry, base_url, data_url, data_url_size);
|
|
|
|
if(status)
|
|
*status = entry->status;
|
|
|
|
if(entry->status)
|
|
return myurl_entry_destroy(entry, true);
|
|
|
|
return entry;
|
|
}
|
|
|
|
/* api */
|
|
/* callback */
|
|
void myurl_callback_memory_set(myurl_t* url, myurl_callback_malloc_f callback_malloc, myurl_callback_realloc_f callback_realloc, myurl_callback_free_f callback_free, void* ctx)
|
|
{
|
|
if(callback_malloc)
|
|
url->callback_malloc = callback_malloc;
|
|
else
|
|
url->callback_malloc = myurl_callback_malloc;
|
|
|
|
if(callback_realloc)
|
|
url->callback_realloc = callback_realloc;
|
|
else
|
|
url->callback_realloc = myurl_callback_realloc;
|
|
|
|
if(callback_free)
|
|
url->callback_free = callback_free;
|
|
else
|
|
url->callback_free = myurl_callback_free;
|
|
|
|
if(ctx)
|
|
url->callback_ctx = ctx;
|
|
else
|
|
url->callback_ctx = url;
|
|
}
|
|
|
|
void * myurl_callback_memory_context(myurl_t* url)
|
|
{
|
|
return url->callback_ctx;
|
|
}
|
|
|
|
/* api entry */
|
|
/* callback for as_string */
|
|
static mystatus_t myurl_entry_host_callback_for_as_string(const char* data, size_t len, void* ctx)
|
|
{
|
|
myurl_utils_serialization_ctx_t *obj_ctx = ctx;
|
|
|
|
if(obj_ctx->error)
|
|
return MyCORE_STATUS_ERROR;
|
|
|
|
if((obj_ctx->length + len + 1) >= obj_ctx->size) {
|
|
size_t new_size = obj_ctx->length + len + 128;
|
|
|
|
char *tmp = obj_ctx->url->callback_realloc(obj_ctx->data, sizeof(char) * new_size,
|
|
obj_ctx->url->callback_ctx);
|
|
|
|
if(tmp) {
|
|
obj_ctx->size = new_size;
|
|
obj_ctx->data = tmp;
|
|
}
|
|
else
|
|
obj_ctx->error = true;
|
|
}
|
|
|
|
memcpy(&obj_ctx->data[ obj_ctx->length ], data, sizeof(char) * len);
|
|
obj_ctx->length += len;
|
|
|
|
return MyCORE_STATUS_OK;
|
|
}
|
|
|
|
static char * myurl_as_string(myurl_entry_t* url_entry, size_t *length, myurl_callback_serialization_func_f func)
|
|
{
|
|
if(length)
|
|
*length = 0;
|
|
|
|
if(url_entry->url_ref == NULL)
|
|
return NULL;
|
|
|
|
myurl_t* url = url_entry->url_ref;
|
|
myurl_utils_serialization_ctx_t ctx = {0};
|
|
|
|
ctx.size = 128;
|
|
ctx.data = url->callback_malloc(sizeof(char) * ctx.size, url->callback_ctx);
|
|
ctx.url = url;
|
|
|
|
if(ctx.data == NULL)
|
|
return NULL;
|
|
|
|
func(url_entry, myurl_entry_host_callback_for_as_string, &ctx);
|
|
|
|
if(ctx.error) {
|
|
if(ctx.data)
|
|
return url->callback_free(ctx.data, url->callback_ctx);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
if(length)
|
|
*length = ctx.length;
|
|
|
|
ctx.data[ ctx.length ] = '\0';
|
|
|
|
return ctx.data;
|
|
}
|
|
|
|
mystatus_t myurl_entry_status(myurl_entry_t* url_entry)
|
|
{
|
|
return url_entry->status;
|
|
}
|
|
|
|
myurl_flags_t myurl_entry_flags(myurl_entry_t* url_entry)
|
|
{
|
|
return url_entry->flags;
|
|
}
|
|
|
|
myurl_t * myurl_entry_url(myurl_entry_t* url_entry)
|
|
{
|
|
return url_entry->url_ref;
|
|
}
|
|
|
|
char * myurl_entry_as_string(myurl_entry_t* url_entry, size_t *length)
|
|
{
|
|
return myurl_as_string(url_entry, length, myurl_serialization_with_fragment);
|
|
}
|
|
|
|
/* scheme */
|
|
myurl_scheme_t * myurl_entry_scheme(myurl_entry_t* url_entry)
|
|
{
|
|
return &url_entry->scheme;
|
|
}
|
|
|
|
const char * myurl_entry_scheme_name(myurl_entry_t* url_entry, size_t* length)
|
|
{
|
|
if(length)
|
|
*length = url_entry->scheme.length;
|
|
|
|
return url_entry->scheme.name;
|
|
}
|
|
|
|
unsigned int myurl_entry_scheme_port(myurl_entry_t* url_entry)
|
|
{
|
|
return (unsigned int)url_entry->scheme.port;
|
|
}
|
|
|
|
myurl_scheme_id_t myurl_entry_scheme_id(myurl_entry_t* url_entry)
|
|
{
|
|
return url_entry->scheme.sid;
|
|
}
|
|
|
|
myurl_scheme_type_t myurl_entry_scheme_type(myurl_entry_t* url_entry)
|
|
{
|
|
return url_entry->scheme.type;
|
|
}
|
|
|
|
/* authority */
|
|
char * myurl_entry_authority_as_string(myurl_entry_t* url_entry, size_t* length)
|
|
{
|
|
return myurl_as_string(url_entry, length, myurl_serialization_authority);
|
|
}
|
|
|
|
const char * myurl_entry_username(myurl_entry_t* url_entry, size_t *length)
|
|
{
|
|
if(length)
|
|
*length = url_entry->username_length;
|
|
|
|
return url_entry->username;
|
|
}
|
|
|
|
const char * myurl_entry_password(myurl_entry_t* url_entry, size_t *length)
|
|
{
|
|
if(length)
|
|
*length = url_entry->password_length;
|
|
|
|
return url_entry->password;
|
|
}
|
|
|
|
/* host */
|
|
myurl_host_t * myurl_entry_host(myurl_entry_t* url_entry)
|
|
{
|
|
return &url_entry->host;
|
|
}
|
|
|
|
char * myurl_entry_host_as_string(myurl_entry_t* url_entry, size_t *length)
|
|
{
|
|
return myurl_as_string(url_entry, length, myurl_serialization_host);
|
|
}
|
|
|
|
myurl_host_type_t myurl_entry_host_type(myurl_entry_t* url_entry)
|
|
{
|
|
return url_entry->host.type;
|
|
}
|
|
|
|
const char * myurl_entry_host_domain(myurl_entry_t* url_entry, size_t *length)
|
|
{
|
|
if(url_entry->host.type != MyURL_HOST_TYPE_DOMAIN) {
|
|
if(length)
|
|
*length = 0;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
if(length)
|
|
*length = url_entry->host.value.domain.length;
|
|
|
|
return url_entry->host.value.domain.value;
|
|
}
|
|
|
|
const char * myurl_entry_host_opaque(myurl_entry_t* url_entry, size_t *length)
|
|
{
|
|
if(url_entry->host.type != MyURL_HOST_TYPE_OPAQUE) {
|
|
if(length)
|
|
*length = 0;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
if(length)
|
|
*length = url_entry->host.value.opaque.length;
|
|
|
|
return url_entry->host.value.opaque.value;
|
|
}
|
|
|
|
unsigned int myurl_entry_host_ipv4(myurl_entry_t* url_entry)
|
|
{
|
|
if(url_entry->host.type != MyURL_HOST_TYPE_IPv4)
|
|
return 0;
|
|
|
|
return url_entry->host.value.ipv.pieces[0];
|
|
}
|
|
|
|
unsigned int * myurl_entry_host_ipv6(myurl_entry_t* url_entry)
|
|
{
|
|
if(url_entry->host.type != MyURL_HOST_TYPE_IPv6)
|
|
return NULL;
|
|
|
|
return url_entry->host.value.ipv.pieces;
|
|
}
|
|
|
|
/* port */
|
|
bool myurl_entry_port_is_defined(myurl_entry_t* url_entry)
|
|
{
|
|
return url_entry->port_is_set;
|
|
}
|
|
|
|
unsigned int myurl_entry_port(myurl_entry_t* url_entry)
|
|
{
|
|
return url_entry->port;
|
|
}
|
|
|
|
/* path */
|
|
myurl_path_t * myurl_entry_path(myurl_entry_t* url_entry)
|
|
{
|
|
return &url_entry->path;
|
|
}
|
|
|
|
char * myurl_entry_path_as_string(myurl_entry_t* url_entry, size_t* length)
|
|
{
|
|
return myurl_as_string(url_entry, length, myurl_serialization_path);
|
|
}
|
|
|
|
size_t myurl_entry_path_length(myurl_entry_t* url_entry)
|
|
{
|
|
return url_entry->path.length;
|
|
}
|
|
|
|
const char * myurl_entry_path_entry(myurl_entry_t* url_entry, size_t index, size_t *length)
|
|
{
|
|
if(url_entry->path.length < index) {
|
|
if(length)
|
|
*length = 0;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
if(length)
|
|
*length = url_entry->path.list[index].length;
|
|
|
|
return url_entry->path.list[index].data;
|
|
}
|
|
|
|
/* query */
|
|
const char * myurl_entry_query(myurl_entry_t* url_entry, size_t *length)
|
|
{
|
|
if(length)
|
|
*length = url_entry->query_length;
|
|
|
|
return url_entry->query;
|
|
}
|
|
|
|
/* fragment */
|
|
const char * myurl_entry_fragment(myurl_entry_t* url_entry, size_t *length)
|
|
{
|
|
if(length)
|
|
*length = url_entry->fragment_length;
|
|
|
|
return url_entry->fragment;
|
|
}
|
|
|
|
/*
|
|
For changes
|
|
*/
|
|
/* scheme */
|
|
const char * myurl_entry_scheme_name_set(myurl_entry_t* url_entry, const char* name, size_t length)
|
|
{
|
|
if(url_entry->url_ref == NULL)
|
|
return NULL;
|
|
|
|
myurl_t* url = url_entry->url_ref;
|
|
|
|
if(myurl_utils_data_copy_set(url, name, length, &url_entry->scheme.name, &url_entry->scheme.length))
|
|
return NULL;
|
|
|
|
return url_entry->scheme.name;
|
|
}
|
|
|
|
void myurl_entry_scheme_port_set(myurl_entry_t* url_entry, unsigned int port)
|
|
{
|
|
url_entry->scheme.port = port;
|
|
}
|
|
|
|
void myurl_entry_scheme_id_set(myurl_entry_t* url_entry, myurl_scheme_id_t sid)
|
|
{
|
|
url_entry->scheme.sid = sid;
|
|
}
|
|
|
|
void myurl_entry_scheme_type_set(myurl_entry_t* url_entry, myurl_scheme_type_t type)
|
|
{
|
|
url_entry->scheme.type = type;
|
|
}
|
|
|
|
void myurl_entry_scheme_clean(myurl_entry_t* url_entry)
|
|
{
|
|
if(url_entry->url_ref)
|
|
myurl_scheme_clean(url_entry->url_ref, &url_entry->scheme);
|
|
}
|
|
|
|
/* authority */
|
|
const char * myurl_entry_username_set(myurl_entry_t* url_entry, const char* username, size_t length)
|
|
{
|
|
if(url_entry->url_ref == NULL)
|
|
return NULL;
|
|
|
|
myurl_t* url = url_entry->url_ref;
|
|
|
|
if(myurl_utils_data_copy_set(url, username, length, &url_entry->username, &url_entry->username_length))
|
|
return NULL;
|
|
|
|
return url_entry->username;
|
|
}
|
|
|
|
const char * myurl_entry_password_set(myurl_entry_t* url_entry, const char* password, size_t length)
|
|
{
|
|
if(url_entry->url_ref == NULL)
|
|
return NULL;
|
|
|
|
myurl_t* url = url_entry->url_ref;
|
|
|
|
if(myurl_utils_data_copy_set(url, password, length, &url_entry->password, &url_entry->password_length))
|
|
return NULL;
|
|
|
|
return url_entry->password;
|
|
}
|
|
|
|
/* host */
|
|
mystatus_t myurl_entry_host_set(myurl_entry_t* url_entry, const char* host, size_t length)
|
|
{
|
|
if(url_entry->url_ref == NULL)
|
|
return MyURL_STATUS_ERROR;
|
|
|
|
myurl_host_t new_host;
|
|
memset(&new_host, 0, sizeof(myurl_host_t));
|
|
|
|
mystatus_t status = myurl_host_parser(url_entry->url_ref, &new_host, host, length, (url_entry->scheme.type & MyURL_SCHEME_TYPE_SPECIAL));
|
|
|
|
if(status)
|
|
return status;
|
|
|
|
myurl_host_clean(url_entry->url_ref, &url_entry->host);
|
|
url_entry->host = new_host;
|
|
|
|
return status;
|
|
}
|
|
|
|
/* port */
|
|
void myurl_entry_port_set(myurl_entry_t* url_entry, unsigned int port)
|
|
{
|
|
url_entry->port = port;
|
|
url_entry->port_is_set = true;
|
|
}
|
|
|
|
/* path */
|
|
mystatus_t myurl_entry_path_set(myurl_entry_t* url_entry, const char* path, size_t length)
|
|
{
|
|
if(url_entry->url_ref == NULL || path == NULL)
|
|
return MyURL_STATUS_ERROR;
|
|
|
|
mystatus_t status;
|
|
myurl_entry_t *new_entry = myurl_parse(url_entry->url_ref, path, length, url_entry, &status);
|
|
|
|
if(new_entry) {
|
|
status = myurl_path_copy(url_entry->url_ref, &new_entry->path, &url_entry->path);
|
|
|
|
myurl_entry_destroy(new_entry, true);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
mystatus_t myurl_entry_path_append_entry(myurl_entry_t* url_entry, const char* entry, size_t length)
|
|
{
|
|
if(url_entry->url_ref == NULL || entry == NULL)
|
|
return MyURL_STATUS_ERROR;
|
|
|
|
size_t buffer_length;
|
|
char *buffer = myurl_utils_percent_encode(url_entry->url_ref, entry, length, myurl_resources_static_map_path, &buffer_length);
|
|
|
|
if(buffer == NULL)
|
|
return MyURL_STATUS_ERROR_MEMORY_ALLOCATION;
|
|
|
|
if(myurl_path_push(url_entry->url_ref, &url_entry->path, buffer, buffer_length) == NULL)
|
|
return MyURL_STATUS_ERROR_MEMORY_ALLOCATION;
|
|
|
|
return MyURL_STATUS_OK;
|
|
}
|
|
|
|
void myurl_entry_path_pop_entry(myurl_entry_t* url_entry)
|
|
{
|
|
myurl_path_pop(&url_entry->path);
|
|
}
|
|
|
|
mystatus_t myurl_entry_path_replace_entry(myurl_entry_t* url_entry, size_t index, const char* entry, size_t length)
|
|
{
|
|
if(url_entry->url_ref == NULL || entry == NULL)
|
|
return MyURL_STATUS_ERROR;
|
|
|
|
size_t buffer_length;
|
|
char *buffer = myurl_utils_percent_encode(url_entry->url_ref, entry, length, myurl_resources_static_map_path, &buffer_length);
|
|
|
|
if(buffer == NULL)
|
|
return MyURL_STATUS_ERROR_MEMORY_ALLOCATION;
|
|
|
|
if(myurl_path_push_to_index(url_entry->url_ref, &url_entry->path, index, buffer, buffer_length) == NULL)
|
|
return MyURL_STATUS_ERROR_MEMORY_ALLOCATION;
|
|
|
|
return MyURL_STATUS_OK;
|
|
}
|
|
|
|
void myurl_entry_path_remove_entry(myurl_entry_t* url_entry, size_t index)
|
|
{
|
|
if(url_entry->url_ref == NULL)
|
|
return;
|
|
|
|
myurl_path_remove_by_index(url_entry->url_ref, &url_entry->path, index);
|
|
}
|
|
|
|
void myurl_entry_path_clean(myurl_entry_t* url_entry)
|
|
{
|
|
if(url_entry->url_ref == NULL)
|
|
return;
|
|
|
|
myurl_path_clean(url_entry->url_ref, &url_entry->path);
|
|
}
|
|
|
|
/* query */
|
|
const char * myurl_entry_query_set(myurl_entry_t* url_entry, const char* query, size_t length)
|
|
{
|
|
if(url_entry->url_ref == NULL)
|
|
return NULL;
|
|
|
|
myurl_t* url = url_entry->url_ref;
|
|
|
|
size_t buffer_length;
|
|
char *buffer = myurl_utils_percent_encode(url, query, length, myurl_resources_static_map_query_charset, &buffer_length);
|
|
|
|
if(buffer == NULL)
|
|
return NULL;
|
|
|
|
if(url_entry->query)
|
|
url->callback_free(url_entry->query, url->callback_ctx);
|
|
|
|
url_entry->query = buffer;
|
|
url_entry->query_length = buffer_length;
|
|
|
|
return url_entry->query;
|
|
}
|
|
|
|
/* fragment */
|
|
const char * myurl_entry_fragment_set(myurl_entry_t* url_entry, const char* fragment, size_t length)
|
|
{
|
|
if(url_entry->url_ref == NULL)
|
|
return NULL;
|
|
|
|
myurl_t* url = url_entry->url_ref;
|
|
|
|
size_t buffer_length;
|
|
char *buffer = myurl_utils_percent_encode(url, fragment, length, myurl_resources_static_map_C0, &buffer_length);
|
|
|
|
if(buffer == NULL)
|
|
return NULL;
|
|
|
|
if(url_entry->fragment)
|
|
url->callback_free(url_entry->fragment, url->callback_ctx);
|
|
|
|
url_entry->fragment = buffer;
|
|
url_entry->fragment_length = buffer_length;
|
|
|
|
return url_entry->fragment;
|
|
}
|
|
|
|
/* for free char* */
|
|
void myurl_entry_free_string(myurl_entry_t* url_entry, char* string)
|
|
{
|
|
if(url_entry->url_ref == NULL)
|
|
return;
|
|
|
|
if(string)
|
|
url_entry->url_ref->callback_free(string, url_entry->url_ref->callback_ctx);
|
|
}
|
|
|
|
|