blob: f19bae9de60e862e18efbaa6f1b1eabea8f44702 [file] [log] [blame]
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#include "netops.h"
#include <ctype.h>
#include "git2/errors.h"
#include "posix.h"
#include "buffer.h"
#include "http_parser.h"
#include "global.h"
int gitno_recv(gitno_buffer *buf)
{
return buf->recv(buf);
}
void gitno_buffer_setup_callback(
gitno_buffer *buf,
char *data,
size_t len,
int (*recv)(gitno_buffer *buf), void *cb_data)
{
memset(data, 0x0, len);
buf->data = data;
buf->len = len;
buf->offset = 0;
buf->recv = recv;
buf->cb_data = cb_data;
}
static int recv_stream(gitno_buffer *buf)
{
git_stream *io = (git_stream *) buf->cb_data;
size_t readlen = buf->len - buf->offset;
ssize_t ret;
readlen = min(readlen, INT_MAX);
ret = git_stream_read(io, buf->data + buf->offset, (int)readlen);
if (ret < 0)
return -1;
buf->offset += ret;
return (int)ret;
}
void gitno_buffer_setup_fromstream(git_stream *st, gitno_buffer *buf, char *data, size_t len)
{
memset(data, 0x0, len);
buf->data = data;
buf->len = len;
buf->offset = 0;
buf->recv = recv_stream;
buf->cb_data = st;
}
/* Consume up to ptr and move the rest of the buffer to the beginning */
void gitno_consume(gitno_buffer *buf, const char *ptr)
{
size_t consumed;
assert(ptr - buf->data >= 0);
assert(ptr - buf->data <= (int) buf->len);
consumed = ptr - buf->data;
memmove(buf->data, ptr, buf->offset - consumed);
memset(buf->data + buf->offset, 0x0, buf->len - buf->offset);
buf->offset -= consumed;
}
/* Consume const bytes and move the rest of the buffer to the beginning */
void gitno_consume_n(gitno_buffer *buf, size_t cons)
{
memmove(buf->data, buf->data + cons, buf->len - buf->offset);
memset(buf->data + cons, 0x0, buf->len - buf->offset);
buf->offset -= cons;
}
/* Match host names according to RFC 2818 rules */
int gitno__match_host(const char *pattern, const char *host)
{
for (;;) {
char c = git__tolower(*pattern++);
if (c == '\0')
return *host ? -1 : 0;
if (c == '*') {
c = *pattern;
/* '*' at the end matches everything left */
if (c == '\0')
return 0;
/*
* We've found a pattern, so move towards the next matching
* char. The '.' is handled specially because wildcards aren't
* allowed to cross subdomains.
*/
while(*host) {
char h = git__tolower(*host);
if (c == h)
return gitno__match_host(pattern, host++);
if (h == '.')
return gitno__match_host(pattern, host);
host++;
}
return -1;
}
if (c != git__tolower(*host++))
return -1;
}
return -1;
}
int gitno_connection_data_handle_redirect(
git_net_url *url,
const char *redirect_str,
const char *service_suffix)
{
git_net_url tmp = GIT_NET_URL_INIT;
int error = 0;
assert(url && redirect_str);
if (redirect_str[0] == '/') {
git__free(url->path);
if ((url->path = git__strdup(redirect_str)) == NULL) {
error = -1;
goto done;
}
} else {
git_net_url *original = url;
if ((error = git_net_url_parse(&tmp, redirect_str)) < 0)
goto done;
/* Validate that this is a legal redirection */
if (original->scheme &&
strcmp(original->scheme, tmp.scheme) != 0 &&
strcmp(tmp.scheme, "https") != 0) {
git_error_set(GIT_ERROR_NET, "cannot redirect from '%s' to '%s'",
original->scheme, tmp.scheme);
error = -1;
goto done;
}
if (original->host &&
git__strcasecmp(original->host, tmp.host) != 0) {
git_error_set(GIT_ERROR_NET, "cannot redirect from '%s' to '%s'",
original->host, tmp.host);
error = -1;
goto done;
}
git_net_url_swap(url, &tmp);
}
/* Remove the service suffix if it was given to us */
if (service_suffix) {
const char *service_query = strchr(service_suffix, '?');
size_t suffix_len = service_query ?
(size_t)(service_query - service_suffix) : strlen(service_suffix);
size_t path_len = strlen(url->path);
if (suffix_len && path_len >= suffix_len) {
size_t suffix_offset = path_len - suffix_len;
if (git__strncmp(url->path + suffix_offset, service_suffix, suffix_len) == 0 &&
(!service_query || git__strcmp(url->query, service_query + 1) == 0)) {
/* Ensure we leave a minimum of '/' as the path */
if (suffix_offset == 0)
suffix_offset++;
url->path[suffix_offset] = '\0';
git__free(url->query);
url->query = NULL;
}
}
}
done:
git_net_url_dispose(&tmp);
return error;
}