* 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 "git2.h"
+
#include "smart.h"
+
+#include "git2.h"
+#include "git2/sys/remote.h"
#include "refs.h"
#include "refspec.h"
+#include "proxy.h"
static int git_smart__recv_cb(gitno_buffer *buf)
{
size_t old_len, bytes_read;
int error;
- assert(t->current_stream);
+ GIT_ASSERT(t->current_stream);
old_len = buf->offset;
if (t->packetsize_cb && !t->cancelled.val) {
error = t->packetsize_cb(bytes_read, t->packetsize_payload);
if (error) {
- git_atomic_set(&t->cancelled, 1);
+ git_atomic32_set(&t->cancelled, 1);
return GIT_EUSER;
}
}
t->current_stream = NULL;
}
- if (close_subtransport &&
- t->wrapped->close(t->wrapped) < 0)
- return -1;
-
- return 0;
-}
-
-static int git_smart__set_callbacks(
- git_transport *transport,
- git_transport_message_cb progress_cb,
- git_transport_message_cb error_cb,
- git_transport_certificate_check_cb certificate_check_cb,
- void *message_cb_payload)
-{
- transport_smart *t = (transport_smart *)transport;
-
- t->progress_cb = progress_cb;
- t->error_cb = error_cb;
- t->certificate_check_cb = certificate_check_cb;
- t->message_cb_payload = message_cb_payload;
-
- return 0;
-}
-
-static int http_header_name_length(const char *http_header)
-{
- const char *colon = strchr(http_header, ':');
- if (!colon)
- return 0;
- return colon - http_header;
-}
-
-static bool is_malformed_http_header(const char *http_header)
-{
- const char *c;
- int name_len;
-
- // Disallow \r and \n
- c = strchr(http_header, '\r');
- if (c)
- return true;
- c = strchr(http_header, '\n');
- if (c)
- return true;
-
- // Require a header name followed by :
- name_len = http_header_name_length(http_header);
- if (name_len < 1)
- return true;
-
- return false;
-}
-
-static char *forbidden_custom_headers[] = {
- "User-Agent",
- "Host",
- "Accept",
- "Content-Type",
- "Transfer-Encoding",
- "Content-Length",
-};
-
-static bool is_forbidden_custom_header(const char *custom_header)
-{
- unsigned long i;
- int name_len = http_header_name_length(custom_header);
-
- // Disallow headers that we set
- for (i = 0; i < ARRAY_SIZE(forbidden_custom_headers); i++)
- if (strncmp(forbidden_custom_headers[i], custom_header, name_len) == 0)
- return true;
-
- return false;
-}
-
-static int git_smart__set_custom_headers(
- git_transport *transport,
- const git_strarray *custom_headers)
-{
- transport_smart *t = (transport_smart *)transport;
- size_t i;
-
- if (t->custom_headers.count)
- git_strarray_free(&t->custom_headers);
-
- if (!custom_headers)
- return 0;
+ if (close_subtransport) {
+ git__free(t->url);
+ t->url = NULL;
- for (i = 0; i < custom_headers->count; i++) {
- if (is_malformed_http_header(custom_headers->strings[i])) {
- giterr_set(GITERR_INVALID, "custom HTTP header '%s' is malformed", custom_headers->strings[i]);
- return -1;
- }
- if (is_forbidden_custom_header(custom_headers->strings[i])) {
- giterr_set(GITERR_INVALID, "custom HTTP header '%s' is already set by libgit2", custom_headers->strings[i]);
+ if (t->wrapped->close(t->wrapped) < 0)
return -1;
- }
}
- return git_strarray_copy(&t->custom_headers, custom_headers);
+ return 0;
}
int git_smart__update_heads(transport_smart *t, git_vector *symrefs)
if (symrefs) {
git_refspec *spec;
- git_buf buf = GIT_BUF_INIT;
+ git_str buf = GIT_STR_INIT;
size_t j;
int error = 0;
git_vector_foreach(symrefs, j, spec) {
- git_buf_clear(&buf);
+ git_str_clear(&buf);
if (git_refspec_src_matches(spec, ref->head.name) &&
- !(error = git_refspec_transform(&buf, spec, ref->head.name)))
- ref->head.symref_target = git_buf_detach(&buf);
+ !(error = git_refspec__transform(&buf, spec, ref->head.name))) {
+ git__free(ref->head.symref_target);
+ ref->head.symref_target = git_str_detach(&buf);
+ }
}
- git_buf_free(&buf);
+ git_str_dispose(&buf);
if (error < 0)
return error;
size_t i;
git_vector_foreach(symrefs, i, spec) {
- git_refspec__free(spec);
+ git_refspec__dispose(spec);
git__free(spec);
}
static int git_smart__connect(
git_transport *transport,
const char *url,
- git_cred_acquire_cb cred_acquire_cb,
- void *cred_acquire_payload,
int direction,
- int flags)
+ const git_remote_connect_options *connect_opts)
{
- transport_smart *t = (transport_smart *)transport;
+ transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
git_smart_subtransport_stream *stream;
int error;
git_pkt *pkt;
if (git_smart__reset_stream(t, true) < 0)
return -1;
+ if (git_remote_connect_options_normalize(&t->connect_opts, t->owner->repo, connect_opts) < 0)
+ return -1;
+
t->url = git__strdup(url);
- GITERR_CHECK_ALLOC(t->url);
+ GIT_ERROR_CHECK_ALLOC(t->url);
t->direction = direction;
- t->flags = flags;
- t->cred_acquire_cb = cred_acquire_cb;
- t->cred_acquire_payload = cred_acquire_payload;
- if (GIT_DIRECTION_FETCH == t->direction)
+ if (GIT_DIRECTION_FETCH == t->direction) {
service = GIT_SERVICE_UPLOADPACK_LS;
- else if (GIT_DIRECTION_PUSH == t->direction)
+ } else if (GIT_DIRECTION_PUSH == t->direction) {
service = GIT_SERVICE_RECEIVEPACK_LS;
- else {
- giterr_set(GITERR_NET, "Invalid direction");
+ } else {
+ git_error_set(GIT_ERROR_NET, "invalid direction");
return -1;
}
pkt = (git_pkt *)git_vector_get(&t->refs, 0);
if (!pkt || GIT_PKT_COMMENT != pkt->type) {
- giterr_set(GITERR_NET, "Invalid response");
+ git_error_set(GIT_ERROR_NET, "invalid response");
return -1;
} else {
/* Remove the comment pkt from the list */
/* We now have loaded the refs. */
t->have_refs = 1;
- first = (git_pkt_ref *)git_vector_get(&t->refs, 0);
+ pkt = (git_pkt *)git_vector_get(&t->refs, 0);
+ if (pkt && GIT_PKT_REF != pkt->type) {
+ git_error_set(GIT_ERROR_NET, "invalid response");
+ return -1;
+ }
+ first = (git_pkt_ref *)pkt;
if ((error = git_vector_init(&symrefs, 1, NULL)) < 0)
return error;
/* Detect capabilities */
- if (git_smart__detect_caps(first, &t->caps, &symrefs) < 0)
- return -1;
+ if ((error = git_smart__detect_caps(first, &t->caps, &symrefs)) == 0) {
+ /* If the only ref in the list is capabilities^{} with OID_ZERO, remove it */
+ if (1 == t->refs.length && !strcmp(first->head.name, "capabilities^{}") &&
+ git_oid_is_zero(&first->head.oid)) {
+ git_vector_clear(&t->refs);
+ git_pkt_free((git_pkt *)first);
+ }
- /* If the only ref in the list is capabilities^{} with OID_ZERO, remove it */
- if (1 == t->refs.length && !strcmp(first->head.name, "capabilities^{}") &&
- git_oid_iszero(&first->head.oid)) {
- git_vector_clear(&t->refs);
- git_pkt_free((git_pkt *)first);
+ /* Keep a list of heads for _ls */
+ git_smart__update_heads(t, &symrefs);
+ } else if (error == GIT_ENOTFOUND) {
+ /* There was no ref packet received, or the cap list was empty */
+ error = 0;
+ } else {
+ git_error_set(GIT_ERROR_NET, "invalid response");
+ goto cleanup;
}
- /* Keep a list of heads for _ls */
- git_smart__update_heads(t, &symrefs);
+ if (t->rpc && (error = git_smart__reset_stream(t, false)) < 0)
+ goto cleanup;
+
+ /* We're now logically connected. */
+ t->connected = 1;
+cleanup:
free_symrefs(&symrefs);
- if (t->rpc && git_smart__reset_stream(t, false) < 0)
+ return error;
+}
+
+static int git_smart__set_connect_opts(
+ git_transport *transport,
+ const git_remote_connect_options *opts)
+{
+ transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
+
+ if (!t->connected) {
+ git_error_set(GIT_ERROR_NET, "cannot reconfigure a transport that is not connected");
return -1;
+ }
- /* We're now logically connected. */
- t->connected = 1;
+ return git_remote_connect_options_normalize(&t->connect_opts, t->owner->repo, opts);
+}
+
+static int git_smart__capabilities(unsigned int *capabilities, git_transport *transport)
+{
+ transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
+
+ *capabilities = 0;
+
+ if (t->caps.want_tip_sha1)
+ *capabilities |= GIT_REMOTE_CAPABILITY_TIP_OID;
+
+ if (t->caps.want_reachable_sha1)
+ *capabilities |= GIT_REMOTE_CAPABILITY_REACHABLE_OID;
return 0;
}
static int git_smart__ls(const git_remote_head ***out, size_t *size, git_transport *transport)
{
- transport_smart *t = (transport_smart *)transport;
+ transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
if (!t->have_refs) {
- giterr_set(GITERR_NET, "The transport has not yet loaded the refs");
+ git_error_set(GIT_ERROR_NET, "the transport has not yet loaded the refs");
return -1;
}
int git_smart__negotiation_step(git_transport *transport, void *data, size_t len)
{
- transport_smart *t = (transport_smart *)transport;
+ transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
git_smart_subtransport_stream *stream;
int error;
return -1;
if (GIT_DIRECTION_FETCH != t->direction) {
- giterr_set(GITERR_NET, "This operation is only valid for fetch");
+ git_error_set(GIT_ERROR_NET, "this operation is only valid for fetch");
return -1;
}
return error;
/* If this is a stateful implementation, the stream we get back should be the same */
- assert(t->rpc || t->current_stream == stream);
+ GIT_ASSERT(t->rpc || t->current_stream == stream);
/* Save off the current stream (i.e. socket) that we are working with */
t->current_stream = stream;
return -1;
if (GIT_DIRECTION_PUSH != t->direction) {
- giterr_set(GITERR_NET, "This operation is only valid for push");
+ git_error_set(GIT_ERROR_NET, "this operation is only valid for push");
return -1;
}
return error;
/* If this is a stateful implementation, the stream we get back should be the same */
- assert(t->rpc || t->current_stream == *stream);
+ GIT_ASSERT(t->rpc || t->current_stream == *stream);
/* Save off the current stream (i.e. socket) that we are working with */
t->current_stream = *stream;
static void git_smart__cancel(git_transport *transport)
{
- transport_smart *t = (transport_smart *)transport;
+ transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
- git_atomic_set(&t->cancelled, 1);
+ git_atomic32_set(&t->cancelled, 1);
}
static int git_smart__is_connected(git_transport *transport)
{
- transport_smart *t = (transport_smart *)transport;
+ transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
return t->connected;
}
-static int git_smart__read_flags(git_transport *transport, int *flags)
-{
- transport_smart *t = (transport_smart *)transport;
-
- *flags = t->flags;
-
- return 0;
-}
-
static int git_smart__close(git_transport *transport)
{
- transport_smart *t = (transport_smart *)transport;
+ transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
git_vector *common = &t->common;
unsigned int i;
git_pkt *p;
static void git_smart__free(git_transport *transport)
{
- transport_smart *t = (transport_smart *)transport;
+ transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
git_vector *refs = &t->refs;
unsigned int i;
git_pkt *p;
git_vector_free(refs);
- git_strarray_free(&t->custom_headers);
+ git_remote_connect_options_dispose(&t->connect_opts);
git__free(t);
}
int git_transport_smart_certificate_check(git_transport *transport, git_cert *cert, int valid, const char *hostname)
{
- transport_smart *t = (transport_smart *)transport;
+ transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
+ git_remote_connect_options *connect_opts = &t->connect_opts;
- return t->certificate_check_cb(cert, valid, hostname, t->message_cb_payload);
+ GIT_ASSERT_ARG(transport);
+ GIT_ASSERT_ARG(cert);
+ GIT_ASSERT_ARG(hostname);
+
+ if (!connect_opts->callbacks.certificate_check)
+ return GIT_PASSTHROUGH;
+
+ return connect_opts->callbacks.certificate_check(cert, valid, hostname, connect_opts->callbacks.payload);
}
-int git_transport_smart_credentials(git_cred **out, git_transport *transport, const char *user, int methods)
+int git_transport_smart_credentials(git_credential **out, git_transport *transport, const char *user, int methods)
{
- transport_smart *t = (transport_smart *)transport;
+ transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
+ git_remote_connect_options *connect_opts = &t->connect_opts;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(transport);
+
+ if (!connect_opts->callbacks.credentials)
+ return GIT_PASSTHROUGH;
- return t->cred_acquire_cb(out, t->url, user, methods, t->cred_acquire_payload);
+ return connect_opts->callbacks.credentials(out, t->url, user, methods, connect_opts->callbacks.payload);
}
int git_transport_smart(git_transport **out, git_remote *owner, void *param)
return -1;
t = git__calloc(1, sizeof(transport_smart));
- GITERR_CHECK_ALLOC(t);
+ GIT_ERROR_CHECK_ALLOC(t);
t->parent.version = GIT_TRANSPORT_VERSION;
- t->parent.set_callbacks = git_smart__set_callbacks;
- t->parent.set_custom_headers = git_smart__set_custom_headers;
t->parent.connect = git_smart__connect;
+ t->parent.set_connect_opts = git_smart__set_connect_opts;
+ t->parent.capabilities = git_smart__capabilities;
t->parent.close = git_smart__close;
t->parent.free = git_smart__free;
t->parent.negotiate_fetch = git_smart__negotiate_fetch;
t->parent.push = git_smart__push;
t->parent.ls = git_smart__ls;
t->parent.is_connected = git_smart__is_connected;
- t->parent.read_flags = git_smart__read_flags;
t->parent.cancel = git_smart__cancel;
t->owner = owner;