mirror of
https://git.proxmox.com/git/libgit2
synced 2025-12-26 11:11:45 +00:00
Instad of each transport having its own function and logic to get to its refs, store them directly in transport. Leverage the new gitno_buffer to make the parsing and storing of the refs use common code and get rid of the git_protocol struct.
246 lines
5.0 KiB
C
246 lines
5.0 KiB
C
/*
|
|
* Copyright (C) 2009-2012 the libgit2 contributors
|
|
*
|
|
* 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/net.h"
|
|
#include "git2/common.h"
|
|
#include "git2/types.h"
|
|
#include "git2/errors.h"
|
|
#include "git2/net.h"
|
|
#include "git2/revwalk.h"
|
|
|
|
#include "vector.h"
|
|
#include "transport.h"
|
|
#include "pkt.h"
|
|
#include "common.h"
|
|
#include "netops.h"
|
|
#include "filebuf.h"
|
|
#include "repository.h"
|
|
#include "fetch.h"
|
|
#include "protocol.h"
|
|
|
|
typedef struct {
|
|
git_transport parent;
|
|
char buff[1024];
|
|
#ifdef GIT_WIN32
|
|
WSADATA wsd;
|
|
#endif
|
|
} transport_git;
|
|
|
|
/*
|
|
* Create a git procol request.
|
|
*
|
|
* For example: 0035git-upload-pack /libgit2/libgit2\0host=github.com\0
|
|
*/
|
|
static int gen_proto(git_buf *request, const char *cmd, const char *url)
|
|
{
|
|
char *delim, *repo;
|
|
char default_command[] = "git-upload-pack";
|
|
char host[] = "host=";
|
|
size_t len;
|
|
|
|
delim = strchr(url, '/');
|
|
if (delim == NULL) {
|
|
giterr_set(GITERR_NET, "Malformed URL");
|
|
return -1;
|
|
}
|
|
|
|
repo = delim;
|
|
|
|
delim = strchr(url, ':');
|
|
if (delim == NULL)
|
|
delim = strchr(url, '/');
|
|
|
|
if (cmd == NULL)
|
|
cmd = default_command;
|
|
|
|
len = 4 + strlen(cmd) + 1 + strlen(repo) + 1 + strlen(host) + (delim - url) + 1;
|
|
|
|
git_buf_grow(request, len);
|
|
git_buf_printf(request, "%04x%s %s%c%s",
|
|
(unsigned int)(len & 0x0FFFF), cmd, repo, 0, host);
|
|
git_buf_put(request, url, delim - url);
|
|
git_buf_putc(request, '\0');
|
|
|
|
if (git_buf_oom(request))
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int send_request(git_transport *t, const char *cmd, const char *url)
|
|
{
|
|
int error;
|
|
git_buf request = GIT_BUF_INIT;
|
|
|
|
error = gen_proto(&request, cmd, url);
|
|
if (error < 0)
|
|
goto cleanup;
|
|
|
|
error = gitno_send(t, request.ptr, request.size, 0);
|
|
|
|
cleanup:
|
|
git_buf_free(&request);
|
|
return error;
|
|
}
|
|
|
|
/*
|
|
* Parse the URL and connect to a server, storing the socket in
|
|
* out. For convenience this also takes care of asking for the remote
|
|
* refs
|
|
*/
|
|
static int do_connect(transport_git *t, const char *url)
|
|
{
|
|
char *host, *port;
|
|
const char prefix[] = "git://";
|
|
|
|
if (!git__prefixcmp(url, prefix))
|
|
url += strlen(prefix);
|
|
|
|
if (gitno_extract_host_and_port(&host, &port, url, GIT_DEFAULT_PORT) < 0)
|
|
return -1;
|
|
|
|
if (gitno_connect((git_transport *)t, host, port) < 0)
|
|
goto on_error;
|
|
|
|
if (send_request((git_transport *)t, NULL, url) < 0)
|
|
goto on_error;
|
|
|
|
git__free(host);
|
|
git__free(port);
|
|
|
|
return 0;
|
|
|
|
on_error:
|
|
git__free(host);
|
|
git__free(port);
|
|
gitno_close(t->parent.socket);
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Since this is a network connection, we need to parse and store the
|
|
* pkt-lines at this stage and keep them there.
|
|
*/
|
|
static int git_connect(git_transport *transport, int direction)
|
|
{
|
|
transport_git *t = (transport_git *) transport;
|
|
|
|
if (direction == GIT_DIR_PUSH) {
|
|
giterr_set(GITERR_NET, "Pushing over git:// is not supported");
|
|
return -1;
|
|
}
|
|
|
|
t->parent.direction = direction;
|
|
|
|
/* Connect and ask for the refs */
|
|
if (do_connect(t, transport->url) < 0)
|
|
return -1;
|
|
|
|
gitno_buffer_setup(transport, &transport->buffer, t->buff, sizeof(t->buff));
|
|
|
|
t->parent.connected = 1;
|
|
if (git_protocol_store_refs(transport, 1) < 0)
|
|
return -1;
|
|
|
|
if (git_protocol_detect_caps(git_vector_get(&transport->refs, 0), &transport->caps) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int git_negotiation_step(struct git_transport *transport, void *data, size_t len)
|
|
{
|
|
return gitno_send(transport, data, len, 0);
|
|
}
|
|
|
|
static int git_close(git_transport *t)
|
|
{
|
|
git_buf buf = GIT_BUF_INIT;
|
|
|
|
if (git_pkt_buffer_flush(&buf) < 0)
|
|
return -1;
|
|
/* Can't do anything if there's an error, so don't bother checking */
|
|
gitno_send(t, buf.ptr, buf.size, 0);
|
|
git_buf_free(&buf);
|
|
|
|
if (gitno_close(t->socket) < 0) {
|
|
giterr_set(GITERR_NET, "Failed to close socket");
|
|
return -1;
|
|
}
|
|
|
|
t->connected = 0;
|
|
|
|
#ifdef GIT_WIN32
|
|
WSACleanup();
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void git_free(git_transport *transport)
|
|
{
|
|
transport_git *t = (transport_git *) transport;
|
|
git_vector *refs = &transport->refs;
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < refs->length; ++i) {
|
|
git_pkt *p = git_vector_get(refs, i);
|
|
git_pkt_free(p);
|
|
}
|
|
git_vector_free(refs);
|
|
|
|
refs = &transport->common;
|
|
for (i = 0; i < refs->length; ++i) {
|
|
git_pkt *p = git_vector_get(refs, i);
|
|
git_pkt_free(p);
|
|
}
|
|
git_vector_free(refs);
|
|
|
|
git__free(t->parent.url);
|
|
git__free(t);
|
|
}
|
|
|
|
int git_transport_git(git_transport **out)
|
|
{
|
|
transport_git *t;
|
|
#ifdef GIT_WIN32
|
|
int ret;
|
|
#endif
|
|
|
|
t = git__malloc(sizeof(transport_git));
|
|
GITERR_CHECK_ALLOC(t);
|
|
|
|
memset(t, 0x0, sizeof(transport_git));
|
|
if (git_vector_init(&t->parent.common, 8, NULL))
|
|
goto on_error;
|
|
|
|
if (git_vector_init(&t->parent.refs, 16, NULL) < 0)
|
|
goto on_error;
|
|
|
|
t->parent.connect = git_connect;
|
|
t->parent.negotiation_step = git_negotiation_step;
|
|
t->parent.close = git_close;
|
|
t->parent.free = git_free;
|
|
|
|
*out = (git_transport *) t;
|
|
|
|
#ifdef GIT_WIN32
|
|
ret = WSAStartup(MAKEWORD(2,2), &t->wsd);
|
|
if (ret != 0) {
|
|
git_free(*out);
|
|
giterr_set(GITERR_NET, "Winsock init failed");
|
|
return -1;
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
|
|
on_error:
|
|
git__free(t);
|
|
return -1;
|
|
}
|