summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--CMakeLists.txt11
-rw-r--r--README.md2
-rw-r--r--include/git2/blob.h42
-rw-r--r--include/git2/errors.h1
-rw-r--r--include/git2/remote.h9
-rw-r--r--src/blob.c71
-rw-r--r--src/common.h1
-rw-r--r--src/config.c1
-rw-r--r--src/fetch.c4
-rw-r--r--src/fetch.h2
-rw-r--r--src/filebuf.c5
-rw-r--r--src/filebuf.h1
-rw-r--r--src/netops.c322
-rw-r--r--src/netops.h13
-rw-r--r--src/pkt.c6
-rw-r--r--src/remote.c10
-rw-r--r--src/remote.h3
-rw-r--r--src/repository.c74
-rw-r--r--src/transport.c2
-rw-r--r--src/transport.h24
-rw-r--r--src/transports/git.c53
-rw-r--r--src/transports/http.c83
-rw-r--r--src/util.c2
-rw-r--r--src/win32/msvc-compat.h1
-rw-r--r--tests-clar/core/errors.c2
-rw-r--r--tests-clar/diff/iterator.c8
-rw-r--r--tests-clar/diff/workdir.c20
-rw-r--r--tests-clar/network/remotes.c2
-rw-r--r--tests-clar/object/blob/fromchunks.c87
-rw-r--r--tests-clar/repo/init.c68
-rw-r--r--tests-clar/resources/status/这1
-rw-r--r--tests-clar/status/status_data.h16
33 files changed, 819 insertions, 129 deletions
diff --git a/.gitignore b/.gitignore
index efc1524e6..45d7b1957 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
/tests-clar/clar.h
/tests-clar/clar_main.c
+/tests-clar/clar_main.c.rule
/apidocs
/trash-*.exe
/libgit2.pc
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8018ea72d..b09729364 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -89,6 +89,13 @@ IF (NOT CMAKE_BUILD_TYPE)
SET(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE)
ENDIF ()
+FIND_PACKAGE(OpenSSL)
+IF (OPENSSL_FOUND)
+ ADD_DEFINITIONS(-DGIT_SSL)
+ INCLUDE_DIRECTORIES(${OPENSSL_INCLUDE_DIR})
+ SET(SSL_LIBRARIES ${OPENSSL_LIBRARIES})
+ENDIF()
+
IF (THREADSAFE)
IF (NOT WIN32)
find_package(Threads REQUIRED)
@@ -121,7 +128,7 @@ ELSEIF (CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)")
TARGET_LINK_LIBRARIES(git2 socket nsl)
ENDIF ()
-TARGET_LINK_LIBRARIES(git2 ${CMAKE_THREAD_LIBS_INIT})
+TARGET_LINK_LIBRARIES(git2 ${CMAKE_THREAD_LIBS_INIT} ${SSL_LIBRARIES})
SET_TARGET_PROPERTIES(git2 PROPERTIES VERSION ${LIBGIT2_VERSION_STRING})
SET_TARGET_PROPERTIES(git2 PROPERTIES SOVERSION ${LIBGIT2_VERSION_MAJOR})
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/libgit2.pc.in ${CMAKE_CURRENT_BINARY_DIR}/libgit2.pc @ONLY)
@@ -157,7 +164,7 @@ IF (BUILD_CLAR)
WORKING_DIRECTORY ${CLAR_PATH}
)
ADD_EXECUTABLE(libgit2_clar ${SRC} ${CLAR_PATH}/clar_main.c ${SRC_TEST} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX})
- TARGET_LINK_LIBRARIES(libgit2_clar ${CMAKE_THREAD_LIBS_INIT})
+ TARGET_LINK_LIBRARIES(libgit2_clar ${CMAKE_THREAD_LIBS_INIT} ${SSL_LIBRARIES})
IF (WIN32)
TARGET_LINK_LIBRARIES(libgit2_clar ws2_32)
ELSEIF (CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)")
diff --git a/README.md b/README.md
index 29b800100..4c23fc870 100644
--- a/README.md
+++ b/README.md
@@ -89,7 +89,7 @@ Here are the bindings to libgit2 that are currently available:
* Go
* go-git <https://github.com/str1ngs/go-git>
* GObject
- * libgit2-glib <https://github.com/nacho/libgit2-glib>
+ * libgit2-glib <http://git.gnome.org/browse/libgit2-glib>
* Haskell
* hgit2 <https://github.com/norm2782/hgit2>
* Lua
diff --git a/include/git2/blob.h b/include/git2/blob.h
index 551770678..544dc7c41 100644
--- a/include/git2/blob.h
+++ b/include/git2/blob.h
@@ -115,6 +115,48 @@ GIT_EXTERN(int) git_blob_create_fromfile(git_oid *oid, git_repository *repo, con
*/
GIT_EXTERN(int) git_blob_create_fromdisk(git_oid *oid, git_repository *repo, const char *path);
+/**
+ * Write a loose blob to the Object Database from a
+ * provider of chunks of data.
+ *
+ * Provided the `hintpath` parameter is filled, its value
+ * will help to determine what git filters should be applied
+ * to the object before it can be placed to the object database.
+ *
+ *
+ * The implementation of the callback has to respect the
+ * following rules:
+ *
+ * - `content` will have to be filled by the consumer. The maximum number
+ * of bytes that the buffer can accept per call is defined by the
+ * `max_length` parameter. Allocation and freeing of the buffer will be taken
+ * care of by the function.
+ *
+ * - The callback is expected to return the number of bytes
+ * that `content` have been filled with.
+ *
+ * - When there is no more data to stream, the callback should
+ * return 0. This will prevent it from being invoked anymore.
+ *
+ * - When an error occurs, the callback should return -1.
+ *
+ *
+ * @param oid Return the id of the written blob
+ *
+ * @param repo repository where the blob will be written.
+ * This repository can be bare or not.
+ *
+ * @param hintpath if not NULL, will help selecting the filters
+ * to apply onto the content of the blob to be created.
+ *
+ * @return GIT_SUCCESS or an error code
+ */
+GIT_EXTERN(int) git_blob_create_fromchunks(
+ git_oid *oid,
+ git_repository *repo,
+ const char *hintpath,
+ int (*source_cb)(char *content, size_t max_length, void *payload),
+ void *payload);
/**
* Write an in-memory buffer to the ODB as a blob
diff --git a/include/git2/errors.h b/include/git2/errors.h
index fb6670004..ccbc9fcf4 100644
--- a/include/git2/errors.h
+++ b/include/git2/errors.h
@@ -88,6 +88,7 @@ typedef enum {
GITERR_TAG,
GITERR_TREE,
GITERR_INDEXER,
+ GITERR_SSL,
} git_error_t;
/**
diff --git a/include/git2/remote.h b/include/git2/remote.h
index 7a032dbce..5c01949d2 100644
--- a/include/git2/remote.h
+++ b/include/git2/remote.h
@@ -229,6 +229,15 @@ GIT_EXTERN(int) git_remote_list(git_strarray *remotes_list, git_repository *repo
*/
GIT_EXTERN(int) git_remote_add(git_remote **out, git_repository *repo, const char *name, const char *url);
+/**
+ * Choose whether to check the server's certificate (applies to HTTPS only)
+ *
+ * @param remote the remote to configure
+ * @param check whether to check the server's certificate (defaults to yes)
+ */
+
+GIT_EXTERN(void) git_remote_check_cert(git_remote *remote, int check);
+
/** @} */
GIT_END_DECL
#endif
diff --git a/src/blob.c b/src/blob.c
index e25944b91..699adec6b 100644
--- a/src/blob.c
+++ b/src/blob.c
@@ -148,27 +148,31 @@ static int write_symlink(
return error;
}
-static int blob_create_internal(git_oid *oid, git_repository *repo, const char *path)
+static int blob_create_internal(git_oid *oid, git_repository *repo, const char *content_path, const char *hint_path, bool try_load_filters)
{
int error;
struct stat st;
git_odb *odb = NULL;
git_off_t size;
- if ((error = git_path_lstat(path, &st)) < 0 || (error = git_repository_odb__weakptr(&odb, repo)) < 0)
+ assert(hint_path || !try_load_filters);
+
+ if ((error = git_path_lstat(content_path, &st)) < 0 || (error = git_repository_odb__weakptr(&odb, repo)) < 0)
return error;
size = st.st_size;
if (S_ISLNK(st.st_mode)) {
- error = write_symlink(oid, odb, path, (size_t)size);
+ error = write_symlink(oid, odb, content_path, (size_t)size);
} else {
git_vector write_filters = GIT_VECTOR_INIT;
- int filter_count;
+ int filter_count = 0;
- /* Load the filters for writing this file to the ODB */
- filter_count = git_filters_load(
- &write_filters, repo, path, GIT_FILTER_TO_ODB);
+ if (try_load_filters) {
+ /* Load the filters for writing this file to the ODB */
+ filter_count = git_filters_load(
+ &write_filters, repo, hint_path, GIT_FILTER_TO_ODB);
+ }
if (filter_count < 0) {
/* Negative value means there was a critical error */
@@ -176,10 +180,10 @@ static int blob_create_internal(git_oid *oid, git_repository *repo, const char *
} else if (filter_count == 0) {
/* No filters need to be applied to the document: we can stream
* directly from disk */
- error = write_file_stream(oid, odb, path, size);
+ error = write_file_stream(oid, odb, content_path, size);
} else {
/* We need to apply one or more filters */
- error = write_file_filtered(oid, odb, path, &write_filters);
+ error = write_file_filtered(oid, odb, content_path, &write_filters);
}
git_filters_free(&write_filters);
@@ -216,7 +220,7 @@ int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *pat
return -1;
}
- error = blob_create_internal(oid, repo, git_buf_cstr(&full_path));
+ error = blob_create_internal(oid, repo, git_buf_cstr(&full_path), git_buf_cstr(&full_path), true);
git_buf_free(&full_path);
return error;
@@ -232,8 +236,53 @@ int git_blob_create_fromdisk(git_oid *oid, git_repository *repo, const char *pat
return error;
}
- error = blob_create_internal(oid, repo, git_buf_cstr(&full_path));
+ error = blob_create_internal(oid, repo, git_buf_cstr(&full_path), git_buf_cstr(&full_path), true);
git_buf_free(&full_path);
return error;
}
+
+#define BUFFER_SIZE 4096
+
+int git_blob_create_fromchunks(
+ git_oid *oid,
+ git_repository *repo,
+ const char *hintpath,
+ int (*source_cb)(char *content, size_t max_length, void *payload),
+ void *payload)
+{
+ int error = -1, read_bytes;
+ char *content = NULL;
+ git_filebuf file = GIT_FILEBUF_INIT;
+
+ content = git__malloc(BUFFER_SIZE);
+ GITERR_CHECK_ALLOC(content);
+
+ if (git_filebuf_open(&file, hintpath == NULL ? "streamed" : hintpath, GIT_FILEBUF_TEMPORARY) < 0)
+ goto cleanup;
+
+ while (1) {
+ read_bytes = source_cb(content, BUFFER_SIZE, payload);
+
+ assert(read_bytes <= BUFFER_SIZE);
+
+ if (read_bytes <= 0)
+ break;
+
+ if (git_filebuf_write(&file, content, read_bytes) < 0)
+ goto cleanup;
+ }
+
+ if (read_bytes < 0)
+ goto cleanup;
+
+ if (git_filebuf_flush(&file) < 0)
+ goto cleanup;
+
+ error = blob_create_internal(oid, repo, file.path_lock, hintpath, hintpath != NULL);
+
+cleanup:
+ git_filebuf_cleanup(&file);
+ git__free(content);
+ return error;
+}
diff --git a/src/common.h b/src/common.h
index 30757de70..e2a300291 100644
--- a/src/common.h
+++ b/src/common.h
@@ -65,7 +65,6 @@ void giterr_clear(void);
void giterr_set_str(int error_class, const char *string);
void giterr_set_regex(const regex_t *regex, int error_code);
-
#include "util.h"
diff --git a/src/config.c b/src/config.c
index 618202c34..d18b85c30 100644
--- a/src/config.c
+++ b/src/config.c
@@ -391,7 +391,6 @@ int git_config_get_string(const char **out, git_config *cfg, const char *name)
return ret;
}
- giterr_set(GITERR_CONFIG, "Config variable '%s' not found", name);
return GIT_ENOTFOUND;
}
diff --git a/src/fetch.c b/src/fetch.c
index c92cf4ef5..96b263faa 100644
--- a/src/fetch.c
+++ b/src/fetch.c
@@ -110,7 +110,7 @@ int git_fetch_download_pack(git_remote *remote, git_off_t *bytes, git_indexer_st
int git_fetch__download_pack(
const char *buffered,
size_t buffered_size,
- GIT_SOCKET fd,
+ git_transport *t,
git_repository *repo,
git_off_t *bytes,
git_indexer_stats *stats)
@@ -120,7 +120,7 @@ int git_fetch__download_pack(
gitno_buffer buf;
git_indexer_stream *idx;
- gitno_buffer_setup(&buf, buff, sizeof(buff), fd);
+ gitno_buffer_setup(t, &buf, buff, sizeof(buff));
if (memcmp(buffered, "PACK", strlen("PACK"))) {
giterr_set(GITERR_NET, "The pack doesn't start with the signature");
diff --git a/src/fetch.h b/src/fetch.h
index b3192a563..a7f126520 100644
--- a/src/fetch.h
+++ b/src/fetch.h
@@ -12,7 +12,7 @@
int git_fetch_negotiate(git_remote *remote);
int git_fetch_download_pack(git_remote *remote, git_off_t *bytes, git_indexer_stats *stats);
-int git_fetch__download_pack(const char *buffered, size_t buffered_size, GIT_SOCKET fd,
+int git_fetch__download_pack(const char *buffered, size_t buffered_size, git_transport *t,
git_repository *repo, git_off_t *bytes, git_indexer_stats *stats);
int git_fetch_setup_walk(git_revwalk **out, git_repository *repo);
diff --git a/src/filebuf.c b/src/filebuf.c
index 6538aea66..876f8e3e7 100644
--- a/src/filebuf.c
+++ b/src/filebuf.c
@@ -130,6 +130,11 @@ GIT_INLINE(int) flush_buffer(git_filebuf *file)
return result;
}
+int git_filebuf_flush(git_filebuf *file)
+{
+ return flush_buffer(file);
+}
+
static int write_normal(git_filebuf *file, void *source, size_t len)
{
if (len > 0) {
diff --git a/src/filebuf.h b/src/filebuf.h
index 72563b57a..377883147 100644
--- a/src/filebuf.h
+++ b/src/filebuf.h
@@ -81,5 +81,6 @@ int git_filebuf_commit(git_filebuf *lock, mode_t mode);
int git_filebuf_commit_at(git_filebuf *lock, const char *path, mode_t mode);
void git_filebuf_cleanup(git_filebuf *lock);
int git_filebuf_hash(git_oid *oid, git_filebuf *file);
+int git_filebuf_flush(git_filebuf *file);
#endif
diff --git a/src/netops.c b/src/netops.c
index e16cae8e6..e6f3e5627 100644
--- a/src/netops.c
+++ b/src/netops.c
@@ -10,6 +10,7 @@
# include <sys/select.h>
# include <sys/time.h>
# include <netdb.h>
+# include <arpa/inet.h>
#else
# include <winsock2.h>
# include <ws2tcpip.h>
@@ -18,13 +19,19 @@
# endif
#endif
+#ifdef GIT_SSL
+# include <openssl/ssl.h>
+# include <openssl/x509v3.h>
+#endif
+#include <ctype.h>
#include "git2/errors.h"
#include "common.h"
#include "netops.h"
#include "posix.h"
#include "buffer.h"
+#include "transport.h"
#ifdef GIT_WIN32
static void net_set_error(const char *str)
@@ -45,25 +52,68 @@ static void net_set_error(const char *str)
}
#endif
-void gitno_buffer_setup(gitno_buffer *buf, char *data, unsigned int len, GIT_SOCKET fd)
+#ifdef GIT_SSL
+static int ssl_set_error(gitno_ssl *ssl, int error)
+{
+ int err;
+ err = SSL_get_error(ssl->ssl, error);
+ giterr_set(GITERR_NET, "SSL error: %s", ERR_error_string(err, NULL));
+ return -1;
+}
+#endif
+
+void gitno_buffer_setup(git_transport *t, gitno_buffer *buf, char *data, unsigned int len)
{
memset(buf, 0x0, sizeof(gitno_buffer));
memset(data, 0x0, len);
buf->data = data;
buf->len = len;
buf->offset = 0;
- buf->fd = fd;
+ buf->fd = t->socket;
+#ifdef GIT_SSL
+ if (t->encrypt)
+ buf->ssl = &t->ssl;
+#endif
}
+#ifdef GIT_SSL
+static int ssl_recv(gitno_ssl *ssl, void *data, size_t len)
+{
+ int ret;
+
+ do {
+ ret = SSL_read(ssl->ssl, data, len);
+ } while (SSL_get_error(ssl->ssl, ret) == SSL_ERROR_WANT_READ);
+
+ if (ret < 0)
+ return ssl_set_error(ssl, ret);
+
+ return ret;
+}
+#endif
+
int gitno_recv(gitno_buffer *buf)
{
int ret;
+#ifdef GIT_SSL
+ if (buf->ssl != NULL) {
+ if ((ret = ssl_recv(buf->ssl, buf->data + buf->offset, buf->len - buf->offset)) < 0)
+ return -1;
+ } else {
+ ret = p_recv(buf->fd, buf->data + buf->offset, buf->len - buf->offset, 0);
+ if (ret < 0) {
+ net_set_error("Error receiving socket data");
+ return -1;
+ }
+ }
+#else
ret = p_recv(buf->fd, buf->data + buf->offset, buf->len - buf->offset, 0);
if (ret < 0) {
net_set_error("Error receiving socket data");
return -1;
}
+#endif
buf->offset += ret;
return ret;
@@ -92,7 +142,239 @@ void gitno_consume_n(gitno_buffer *buf, size_t cons)
buf->offset -= cons;
}
-int gitno_connect(GIT_SOCKET *sock, const char *host, const char *port)
+int gitno_ssl_teardown(git_transport *t)
+{
+#ifdef GIT_SSL
+ int ret;
+#endif
+
+ if (!t->encrypt)
+ return 0;
+
+#ifdef GIT_SSL
+
+ do {
+ ret = SSL_shutdown(t->ssl.ssl);
+ } while (ret == 0);
+ if (ret < 0)
+ return ssl_set_error(&t->ssl, ret);
+
+ SSL_free(t->ssl.ssl);
+ SSL_CTX_free(t->ssl.ctx);
+#endif
+ return 0;
+}
+
+
+#ifdef GIT_SSL
+/* Match host names according to RFC 2818 rules */
+static int match_host(const char *pattern, const char *host)
+{
+ for (;;) {
+ char c = 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 = tolower(*host);
+ if (c == h)
+ return match_host(pattern, host++);
+ if (h == '.')
+ return match_host(pattern, host);
+ host++;
+ }
+ return -1;
+ }
+
+ if (c != tolower(*host++))
+ return -1;
+ }
+
+ return -1;
+}
+
+static int check_host_name(const char *name, const char *host)
+{
+ if (!strcasecmp(name, host))
+ return 0;
+
+ if (match_host(name, host) < 0)
+ return -1;
+
+ return 0;
+}
+
+static int verify_server_cert(git_transport *t, const char *host)
+{
+ X509 *cert;
+ X509_NAME *peer_name;
+ ASN1_STRING *str;
+ unsigned char *peer_cn = NULL;
+ int matched = -1, type = GEN_DNS;
+ GENERAL_NAMES *alts;
+ struct in6_addr addr6;
+ struct in_addr addr4;
+ void *addr;
+ int i = -1,j;
+
+
+ /* Try to parse the host as an IP address to see if it is */
+ if (inet_pton(AF_INET, host, &addr4)) {
+ type = GEN_IPADD;
+ addr = &addr4;
+ } else {
+ if(inet_pton(AF_INET6, host, &addr6)) {
+ type = GEN_IPADD;
+ addr = &addr6;
+ }
+ }
+
+
+ cert = SSL_get_peer_certificate(t->ssl.ssl);
+
+ /* Check the alternative names */
+ alts = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
+ if (alts) {
+ int num;
+
+ num = sk_GENERAL_NAME_num(alts);
+ for (i = 0; i < num && matched != 1; i++) {
+ const GENERAL_NAME *gn = sk_GENERAL_NAME_value(alts, i);
+ const char *name = (char *) ASN1_STRING_data(gn->d.ia5);
+ size_t namelen = (size_t) ASN1_STRING_length(gn->d.ia5);
+
+ /* Skip any names of a type we're not looking for */
+ if (gn->type != type)
+ continue;
+
+ if (type == GEN_DNS) {
+ /* If it contains embedded NULs, don't even try */
+ if (memchr(name, '\0', namelen))
+ continue;
+
+ if (check_host_name(name, host) < 0)
+ matched = 0;
+ else
+ matched = 1;
+ } else if (type == GEN_IPADD) {
+ /* Here name isn't so much a name but a binary representation of the IP */
+ matched = !!memcmp(name, addr, namelen);
+ }
+ }
+ }
+ GENERAL_NAMES_free(alts);
+
+ if (matched == 0)
+ goto on_error;
+
+ if (matched == 1)
+ return 0;
+
+ /* If no alternative names are available, check the common name */
+ peer_name = X509_get_subject_name(cert);
+ if (peer_name == NULL)
+ goto on_error;
+
+ if (peer_name) {
+ /* Get the index of the last CN entry */
+ while ((j = X509_NAME_get_index_by_NID(peer_name, NID_commonName, i)) >= 0)
+ i = j;
+ }
+
+ if (i < 0)
+ goto on_error;
+
+ str = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(peer_name, i));
+ if (str == NULL)
+ goto on_error;
+
+ /* Work around a bug in OpenSSL whereby ASN1_STRING_to_UTF8 fails if it's already in utf-8 */
+ if (ASN1_STRING_type(str) == V_ASN1_UTF8STRING) {
+ int size = ASN1_STRING_length(str);
+
+ if (size > 0) {
+ peer_cn = OPENSSL_malloc(size + 1);
+ GITERR_CHECK_ALLOC(peer_cn);
+ memcpy(peer_cn, ASN1_STRING_data(str), size);
+ peer_cn[size] = '\0';
+ }
+ } else {
+ int size = ASN1_STRING_to_UTF8(&peer_cn, str);
+ GITERR_CHECK_ALLOC(peer_cn);
+ if (memchr(peer_cn, '\0', size))
+ goto cert_fail;
+ }
+
+ if (check_host_name((char *)peer_cn, host) < 0)
+ goto cert_fail;
+
+ OPENSSL_free(peer_cn);
+
+ return 0;
+
+on_error:
+ OPENSSL_free(peer_cn);
+ return ssl_set_error(&t->ssl, 0);
+
+cert_fail:
+ OPENSSL_free(peer_cn);
+ giterr_set(GITERR_SSL, "Certificate host name check failed");
+ return -1;
+}
+
+static int ssl_setup(git_transport *t, const char *host)
+{
+ int ret;
+
+ SSL_library_init();
+ SSL_load_error_strings();
+ t->ssl.ctx = SSL_CTX_new(SSLv23_method());
+ if (t->ssl.ctx == NULL)
+ return ssl_set_error(&t->ssl, 0);
+
+ SSL_CTX_set_mode(t->ssl.ctx, SSL_MODE_AUTO_RETRY);
+ SSL_CTX_set_verify(t->ssl.ctx, SSL_VERIFY_PEER, NULL);
+ if (!SSL_CTX_set_default_verify_paths(t->ssl.ctx))
+ return ssl_set_error(&t->ssl, 0);
+
+ t->ssl.ssl = SSL_new(t->ssl.ctx);
+ if (t->ssl.ssl == NULL)
+ return ssl_set_error(&t->ssl, 0);
+
+ if((ret = SSL_set_fd(t->ssl.ssl, t->socket)) == 0)
+ return ssl_set_error(&t->ssl, ret);
+
+ if ((ret = SSL_connect(t->ssl.ssl)) <= 0)
+ return ssl_set_error(&t->ssl, ret);
+
+ if (t->check_cert && verify_server_cert(t, host) < 0)
+ return -1;
+
+ return 0;
+}
+#else
+static int ssl_setup(git_transport *t, const char *host)
+{
+ GIT_UNUSED(t);
+ GIT_UNUSED(host);
+ return 0;
+}
+#endif
+
+int gitno_connect(git_transport *t, const char *host, const char *port)
{
struct addrinfo *info = NULL, *p;
struct addrinfo hints;
@@ -129,20 +411,46 @@ int gitno_connect(GIT_SOCKET *sock, const char *host, const char *port)
return -1;
}
+ t->socket = s;
freeaddrinfo(info);
- *sock = s;
+
+ if (t->encrypt && ssl_setup(t, host) < 0)
+ return -1;
+
return 0;
}
-int gitno_send(GIT_SOCKET s, const char *msg, size_t len, int flags)
+#ifdef GIT_SSL
+static int send_ssl(gitno_ssl *ssl, const char *msg, size_t len)
{
int ret;
size_t off = 0;
while (off < len) {
- errno = 0;
+ ret = SSL_write(ssl->ssl, msg + off, len - off);
+ if (ret <= 0)
+ return ssl_set_error(ssl, ret);
- ret = p_send(s, msg + off, len - off, flags);
+ off += ret;
+ }
+
+ return off;
+}
+#endif
+
+int gitno_send(git_transport *t, const char *msg, size_t len, int flags)
+{
+ int ret;
+ size_t off = 0;
+
+#ifdef GIT_SSL
+ if (t->encrypt)
+ return send_ssl(&t->ssl, msg, len);
+#endif
+
+ while (off < len) {
+ errno = 0;
+ ret = p_send(t->socket, msg + off, len - off, flags);
if (ret < 0) {
net_set_error("Error sending data");
return -1;
diff --git a/src/netops.h b/src/netops.h
index 9d13f3891..4976f87f8 100644
--- a/src/netops.h
+++ b/src/netops.h
@@ -8,22 +8,29 @@
#define INCLUDE_netops_h__
#include "posix.h"
+#include "transport.h"
+#include "common.h"
typedef struct gitno_buffer {
char *data;
size_t len;
size_t offset;
GIT_SOCKET fd;
+#ifdef GIT_SSL
+ struct gitno_ssl *ssl;
+#endif
} gitno_buffer;
-void gitno_buffer_setup(gitno_buffer *buf, char *data, unsigned int len, GIT_SOCKET fd);
+void gitno_buffer_setup(git_transport *t, gitno_buffer *buf, char *data, unsigned int len);
int gitno_recv(gitno_buffer *buf);
+
void gitno_consume(gitno_buffer *buf, const char *ptr);
void gitno_consume_n(gitno_buffer *buf, size_t cons);
-int gitno_connect(GIT_SOCKET *s, const char *host, const char *port);
-int gitno_send(GIT_SOCKET s, const char *msg, size_t len, int flags);
+int gitno_connect(git_transport *t, const char *host, const char *port);
+int gitno_send(git_transport *t, const char *msg, size_t len, int flags);
int gitno_close(GIT_SOCKET s);
+int gitno_ssl_teardown(git_transport *t);
int gitno_send_chunk_size(int s, size_t len);
int gitno_select_in(gitno_buffer *buf, long int sec, long int usec);
diff --git a/src/pkt.c b/src/pkt.c
index 95430ddfc..88510f4b1 100644
--- a/src/pkt.c
+++ b/src/pkt.c
@@ -281,12 +281,6 @@ int git_pkt_buffer_flush(git_buf *buf)
return git_buf_put(buf, pkt_flush_str, strlen(pkt_flush_str));
}
-int git_pkt_send_flush(GIT_SOCKET s)
-{
-
- return gitno_send(s, pkt_flush_str, strlen(pkt_flush_str), 0);
-}
-
static int buffer_want_with_caps(git_remote_head *head, git_transport_caps *caps, git_buf *buf)
{
char capstr[20];
diff --git a/src/remote.c b/src/remote.c
index 5993ad02b..8d6076107 100644
--- a/src/remote.c
+++ b/src/remote.c
@@ -66,6 +66,7 @@ int git_remote_new(git_remote **out, git_repository *repo, const char *name, con
memset(remote, 0x0, sizeof(git_remote));
remote->repo = repo;
+ remote->check_cert = 1;
if (git_vector_init(&remote->refs, 32, NULL) < 0)
return -1;
@@ -108,6 +109,7 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name)
GITERR_CHECK_ALLOC(remote);
memset(remote, 0x0, sizeof(git_remote));
+ remote->check_cert = 1;
remote->name = git__strdup(name);
GITERR_CHECK_ALLOC(remote->name);
@@ -291,6 +293,7 @@ int git_remote_connect(git_remote *remote, int direction)
if (git_transport_new(&t, remote->url) < 0)
return -1;
+ t->check_cert = remote->check_cert;
if (t->connect(t, direction) < 0) {
goto on_error;
}
@@ -512,3 +515,10 @@ on_error:
git_remote_free(*out);
return -1;
}
+
+void git_remote_check_cert(git_remote *remote, int check)
+{
+ assert(remote);
+
+ remote->check_cert = check;
+}
diff --git a/src/remote.h b/src/remote.h
index 5a1625d05..0949ad434 100644
--- a/src/remote.h
+++ b/src/remote.h
@@ -19,7 +19,8 @@ struct git_remote {
struct git_refspec push;
git_transport *transport;
git_repository *repo;
- unsigned int need_pack:1;
+ unsigned int need_pack:1,
+ check_cert;
};
#endif
diff --git a/src/repository.c b/src/repository.c
index 5120356bf..718170839 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -655,7 +655,46 @@ static int repo_init_createhead(const char *git_dir)
return 0;
}
-static int repo_init_config(const char *git_dir, int is_bare)
+static bool is_chmod_supported(const char *file_path)
+{
+ struct stat st1, st2;
+ static int _is_supported = -1;
+
+ if (_is_supported > -1)
+ return _is_supported;
+
+ if (p_stat(file_path, &st1) < 0)
+ return false;
+
+ if (p_chmod(file_path, st1.st_mode ^ S_IXUSR) < 0)
+ return false;
+
+ if (p_stat(file_path, &st2) < 0)
+ return false;
+
+ _is_supported = (st1.st_mode != st2.st_mode);
+ return _is_supported;
+}
+
+static bool is_filesystem_case_insensitive(const char *gitdir_path)
+{
+ git_buf path = GIT_BUF_INIT;
+ static int _is_insensitive = -1;
+
+ if (_is_insensitive > -1)
+ return _is_insensitive;
+
+ if (git_buf_joinpath(&path, gitdir_path, "CoNfIg") < 0)
+ goto cleanup;
+
+ _is_insensitive = git_path_exists(git_buf_cstr(&path));
+
+cleanup:
+ git_buf_free(&path);
+ return _is_insensitive;
+}
+
+static int repo_init_config(const char *git_dir, bool is_bare, bool is_reinit)
{
git_buf cfg_path = GIT_BUF_INIT;
git_config *config = NULL;
@@ -670,13 +709,17 @@ static int repo_init_config(const char *git_dir, int is_bare)
if (git_buf_joinpath(&cfg_path, git_dir, GIT_CONFIG_FILENAME_INREPO) < 0)
return -1;
- if (git_config_open_ondisk(&config, cfg_path.ptr) < 0) {
+ if (git_config_open_ondisk(&config, git_buf_cstr(&cfg_path)) < 0) {
git_buf_free(&cfg_path);
return -1;
}
SET_REPO_CONFIG(bool, "core.bare", is_bare);
SET_REPO_CONFIG(int32, "core.repositoryformatversion", GIT_REPO_VERSION);
+ SET_REPO_CONFIG(bool, "core.filemode", is_chmod_supported(git_buf_cstr(&cfg_path)));
+
+ if (!is_reinit && is_filesystem_case_insensitive(git_dir))
+ SET_REPO_CONFIG(bool, "core.ignorecase", true);
/* TODO: what other defaults? */
git_buf_free(&cfg_path);
@@ -790,30 +833,35 @@ static int repo_init_structure(const char *git_dir, int is_bare)
int git_repository_init(git_repository **repo_out, const char *path, unsigned is_bare)
{
git_buf repository_path = GIT_BUF_INIT;
+ bool is_reinit;
+ int result = -1;
assert(repo_out && path);
if (git_buf_joinpath(&repository_path, path, is_bare ? "" : GIT_DIR) < 0)
- return -1;
+ goto cleanup;
- if (git_path_isdir(repository_path.ptr) == true) {
- if (valid_repository_path(&repository_path) == true) {
- int res = repo_init_reinit(repo_out, repository_path.ptr, is_bare);
- git_buf_free(&repository_path);
- return res;
- }
+ is_reinit = git_path_isdir(repository_path.ptr) && valid_repository_path(&repository_path);
+
+ if (is_reinit) {
+ if (repo_init_reinit(repo_out, repository_path.ptr, is_bare) < 0)
+ goto cleanup;
+
+ result = repo_init_config(repository_path.ptr, is_bare, is_reinit);
}
if (repo_init_structure(repository_path.ptr, is_bare) < 0 ||
- repo_init_config(repository_path.ptr, is_bare) < 0 ||
+ repo_init_config(repository_path.ptr, is_bare, is_reinit) < 0 ||
repo_init_createhead(repository_path.ptr) < 0 ||
git_repository_open(repo_out, repository_path.ptr) < 0) {
- git_buf_free(&repository_path);
- return -1;
+ goto cleanup;
}
+ result = 0;
+
+cleanup:
git_buf_free(&repository_path);
- return 0;
+ return result;
}
int git_repository_head_detached(git_repository *repo)
diff --git a/src/transport.c b/src/transport.c
index 5b2cd7ea4..fb2b94946 100644
--- a/src/transport.c
+++ b/src/transport.c
@@ -17,7 +17,7 @@ static struct {
} transports[] = {
{"git://", git_transport_git},
{"http://", git_transport_http},
- {"https://", git_transport_dummy},
+ {"https://", git_transport_https},
{"file://", git_transport_local},
{"git+ssh://", git_transport_dummy},
{"ssh+git://", git_transport_dummy},
diff --git a/src/transport.h b/src/transport.h
index 125df2745..68b92f7a6 100644
--- a/src/transport.h
+++ b/src/transport.h
@@ -10,6 +10,13 @@
#include "git2/net.h"
#include "git2/indexer.h"
#include "vector.h"
+#include "posix.h"
+#include "common.h"
+#ifdef GIT_SSL
+# include <openssl/ssl.h>
+# include <openssl/err.h>
+#endif
+
#define GIT_CAP_OFS_DELTA "ofs-delta"
@@ -18,6 +25,14 @@ typedef struct git_transport_caps {
ofs_delta:1;
} git_transport_caps;
+#ifdef GIT_SSL
+typedef struct gitno_ssl {
+ SSL_CTX *ctx;
+ SSL *ssl;
+} gitno_ssl;
+#endif
+
+
/*
* A day in the life of a network operation
* ========================================
@@ -53,7 +68,13 @@ struct git_transport {
* Whether we want to push or fetch
*/
int direction : 1, /* 0 fetch, 1 push */
- connected : 1;
+ connected : 1,
+ check_cert: 1,
+ encrypt : 1;
+#ifdef GIT_SSL
+ struct gitno_ssl ssl;
+#endif
+ GIT_SOCKET socket;
/**
* Connect and store the remote heads
*/
@@ -94,6 +115,7 @@ int git_transport_new(struct git_transport **transport, const char *url);
int git_transport_local(struct git_transport **transport);
int git_transport_git(struct git_transport **transport);
int git_transport_http(struct git_transport **transport);
+int git_transport_https(struct git_transport **transport);
int git_transport_dummy(struct git_transport **transport);
/**
diff --git a/src/transports/git.c b/src/transports/git.c
index 844b350be..45f571f20 100644
--- a/src/transports/git.c
+++ b/src/transports/git.c
@@ -25,7 +25,6 @@
typedef struct {
git_transport parent;
git_protocol proto;
- GIT_SOCKET socket;
git_vector refs;
git_remote_head **heads;
git_transport_caps caps;
@@ -77,7 +76,7 @@ static int gen_proto(git_buf *request, const char *cmd, const char *url)
return 0;
}
-static int send_request(GIT_SOCKET s, const char *cmd, const char *url)
+static int send_request(git_transport *t, const char *cmd, const char *url)
{
int error;
git_buf request = GIT_BUF_INIT;
@@ -86,7 +85,7 @@ static int send_request(GIT_SOCKET s, const char *cmd, const char *url)
if (error < 0)
goto cleanup;
- error = gitno_send(s, request.ptr, request.size, 0);
+ error = gitno_send(t, request.ptr, request.size, 0);
cleanup:
git_buf_free(&request);
@@ -102,9 +101,6 @@ static int do_connect(transport_git *t, const char *url)
{
char *host, *port;
const char prefix[] = "git://";
- int error;
-
- t->socket = INVALID_SOCKET;
if (!git__prefixcmp(url, prefix))
url += strlen(prefix);
@@ -112,24 +108,22 @@ static int do_connect(transport_git *t, const char *url)
if (gitno_extract_host_and_port(&host, &port, url, GIT_DEFAULT_PORT) < 0)
return -1;
- if ((error = gitno_connect(&t->socket, host, port)) == 0) {
- error = send_request(t->socket, NULL, url);
- }
+ 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);
- if (error < 0 && t->socket != INVALID_SOCKET) {
- gitno_close(t->socket);
- t->socket = INVALID_SOCKET;
- }
-
- if (t->socket == INVALID_SOCKET) {
- giterr_set(GITERR_NET, "Failed to connect to the host");
- return -1;
- }
-
return 0;
+
+on_error:
+ git__free(host);
+ git__free(port);
+ gitno_close(t->parent.socket);
+ return -1;
}
/*
@@ -215,7 +209,7 @@ static int git_connect(git_transport *transport, int direction)
if (do_connect(t, transport->url) < 0)
goto cleanup;
- gitno_buffer_setup(&t->buf, t->buff, sizeof(t->buff), t->socket);
+ gitno_buffer_setup(transport, &t->buf, t->buff, sizeof(t->buff));
t->parent.connected = 1;
if (store_refs(t) < 0)
@@ -308,7 +302,7 @@ static int git_negotiate_fetch(git_transport *transport, git_repository *repo, c
if (git_fetch_setup_walk(&walk, repo) < 0)
goto on_error;
- if (gitno_send(t->socket, data.ptr, data.size, 0) < 0)
+ if (gitno_send(transport, data.ptr, data.size, 0) < 0)
goto on_error;
git_buf_clear(&data);
@@ -328,7 +322,7 @@ static int git_negotiate_fetch(git_transport *transport, git_repository *repo, c
if (git_buf_oom(&data))
goto on_error;
- if (gitno_send(t->socket, data.ptr, data.size, 0) < 0)
+ if (gitno_send(transport, data.ptr, data.size, 0) < 0)
goto on_error;
pkt_type = recv_pkt(buf);
@@ -351,7 +345,7 @@ static int git_negotiate_fetch(git_transport *transport, git_repository *repo, c
git_buf_clear(&data);
git_pkt_buffer_flush(&data);
git_pkt_buffer_done(&data);
- if (gitno_send(t->socket, data.ptr, data.size, 0) < 0)
+ if (gitno_send(transport, data.ptr, data.size, 0) < 0)
goto on_error;
git_buf_free(&data);
@@ -392,7 +386,7 @@ static int git_download_pack(git_transport *transport, git_repository *repo, git
if (pkt->type == GIT_PKT_PACK) {
git__free(pkt);
- return git_fetch__download_pack(buf->data, buf->offset, t->socket, repo, bytes, stats);
+ return git_fetch__download_pack(buf->data, buf->offset, transport, repo, bytes, stats);
}
/* For now we don't care about anything */
@@ -406,18 +400,21 @@ static int git_download_pack(git_transport *transport, git_repository *repo, git
return read_bytes;
}
-static int git_close(git_transport *transport)
+static int git_close(git_transport *t)
{
- transport_git *t = (transport_git*) transport;
+ 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 */
- git_pkt_send_flush(t->socket);
+ gitno_send(t, buf.ptr, buf.size, 0);
+
if (gitno_close(t->socket) < 0) {
giterr_set(GITERR_NET, "Failed to close socket");
return -1;
}
- t->parent.connected = 0;
+ t->connected = 0;
#ifdef GIT_WIN32
WSACleanup();
diff --git a/src/transports/http.c b/src/transports/http.c
index 9ea21a61d..4139a2fa6 100644
--- a/src/transports/http.c
+++ b/src/transports/http.c
@@ -32,7 +32,6 @@ typedef struct {
git_protocol proto;
git_vector refs;
git_vector common;
- GIT_SOCKET socket;
git_buf buf;
git_remote_head **heads;
int error;
@@ -43,6 +42,7 @@ typedef struct {
enum last_cb last_cb;
http_parser parser;
char *content_type;
+ char *path;
char *host;
char *port;
char *service;
@@ -52,12 +52,9 @@ typedef struct {
#endif
} transport_http;
-static int gen_request(git_buf *buf, const char *url, const char *host, const char *op,
+static int gen_request(git_buf *buf, const char *path, const char *host, const char *op,
const char *service, ssize_t content_length, int ls)
{
- const char *path = url;
-
- path = strchr(path, '/');
if (path == NULL) /* Is 'git fetch http://host.com/' valid? */
path = "/";
@@ -85,15 +82,12 @@ static int gen_request(git_buf *buf, const char *url, const char *host, const ch
static int do_connect(transport_http *t, const char *host, const char *port)
{
- GIT_SOCKET s;
-
if (t->parent.connected && http_should_keep_alive(&t->parser))
return 0;
- if (gitno_connect(&s, host, port) < 0)
+ if (gitno_connect((git_transport *) t, host, port) < 0)
return -1;
- t->socket = s;
t->parent.connected = 1;
return 0;
@@ -231,7 +225,7 @@ static int store_refs(transport_http *t)
settings.on_body = on_body_store_refs;
settings.on_message_complete = on_message_complete;
- gitno_buffer_setup(&buf, buffer, sizeof(buffer), t->socket);
+ gitno_buffer_setup((git_transport *)t, &buf, buffer, sizeof(buffer));
while(1) {
size_t parsed;
@@ -267,7 +261,8 @@ static int http_connect(git_transport *transport, int direction)
int ret;
git_buf request = GIT_BUF_INIT;
const char *service = "upload-pack";
- const char *url = t->parent.url, *prefix = "http://";
+ const char *url = t->parent.url, *prefix_http = "http://", *prefix_https = "https://";
+ const char *default_port;
if (direction == GIT_DIR_PUSH) {
giterr_set(GITERR_NET, "Pushing over HTTP is not implemented");
@@ -278,10 +273,19 @@ static int http_connect(git_transport *transport, int direction)
if (git_vector_init(&t->refs, 16, NULL) < 0)
return -1;
- if (!git__prefixcmp(url, prefix))
- url += strlen(prefix);
+ if (!git__prefixcmp(url, prefix_http)) {
+ url = t->parent.url + strlen(prefix_http);
+ default_port = "80";
+ }
+
+ if (!git__prefixcmp(url, prefix_https)) {
+ url += strlen(prefix_https);
+ default_port = "443";
+ }
+
+ t->path = strchr(url, '/');
- if ((ret = gitno_extract_host_and_port(&t->host, &t->port, url, "80")) < 0)
+ if ((ret = gitno_extract_host_and_port(&t->host, &t->port, url, default_port)) < 0)
goto cleanup;
t->service = git__strdup(service);
@@ -291,12 +295,13 @@ static int http_connect(git_transport *transport, int direction)
goto cleanup;
/* Generate and send the HTTP request */
- if ((ret = gen_request(&request, url, t->host, "GET", service, 0, 1)) < 0) {
+ if ((ret = gen_request(&request, t->path, t->host, "GET", service, 0, 1)) < 0) {
giterr_set(GITERR_NET, "Failed to generate request");
goto cleanup;
}
- if ((ret = gitno_send(t->socket, request.ptr, request.size, 0)) < 0)
+
+ if (gitno_send(transport, request.ptr, request.size, 0) < 0)
goto cleanup;
ret = store_refs(t);
@@ -403,7 +408,7 @@ static int parse_response(transport_http *t)
settings.on_body = on_body_parse_response;
settings.on_message_complete = on_message_complete;
- gitno_buffer_setup(&buf, buffer, sizeof(buffer), t->socket);
+ gitno_buffer_setup((git_transport *)t, &buf, buffer, sizeof(buffer));
while(1) {
size_t parsed;
@@ -437,13 +442,9 @@ static int http_negotiate_fetch(git_transport *transport, git_repository *repo,
git_oid oid;
git_pkt_ack *pkt;
git_vector *common = &t->common;
- const char *prefix = "http://", *url = t->parent.url;
git_buf request = GIT_BUF_INIT, data = GIT_BUF_INIT;
- gitno_buffer_setup(&buf, buff, sizeof(buff), t->socket);
- /* TODO: Store url in the transport */
- if (!git__prefixcmp(url, prefix))
- url += strlen(prefix);
+ gitno_buffer_setup(transport, &buf, buff, sizeof(buff));
if (git_vector_init(common, 16, NULL) < 0)
return -1;
@@ -474,13 +475,13 @@ static int http_negotiate_fetch(git_transport *transport, git_repository *repo,
git_pkt_buffer_done(&data);
- if ((ret = gen_request(&request, url, t->host, "POST", "upload-pack", data.size, 0)) < 0)
+ if ((ret = gen_request(&request, t->path, t->host, "POST", "upload-pack", data.size, 0)) < 0)
goto cleanup;
- if ((ret = gitno_send(t->socket, request.ptr, request.size, 0)) < 0)
+ if ((ret = gitno_send(transport, request.ptr, request.size, 0)) < 0)
goto cleanup;
- if ((ret = gitno_send(t->socket, data.ptr, data.size, 0)) < 0)
+ if ((ret = gitno_send(transport, data.ptr, data.size, 0)) < 0)
goto cleanup;
git_buf_clear(&request);
@@ -547,7 +548,7 @@ static int http_download_pack(git_transport *transport, git_repository *repo, gi
git_indexer_stream *idx = NULL;
download_pack_cbdata data;
- gitno_buffer_setup(&buf, buffer, sizeof(buffer), t->socket);
+ gitno_buffer_setup(transport, &buf, buffer, sizeof(buffer));
if (memcmp(oldbuf->ptr, "PACK", strlen("PACK"))) {
giterr_set(GITERR_NET, "The pack doesn't start with a pack signature");
@@ -557,7 +558,6 @@ static int http_download_pack(git_transport *transport, git_repository *repo, gi
if (git_indexer_stream_new(&idx, git_repository_path(repo)) < 0)
return -1;
-
/*
* This is part of the previous response, so we don't want to
* re-init the parser, just set these two callbacks.
@@ -576,6 +576,8 @@ static int http_download_pack(git_transport *transport, git_repository *repo, gi
if (git_indexer_stream_add(idx, git_buf_cstr(oldbuf), git_buf_len(oldbuf), stats) < 0)
goto on_error;
+ gitno_buffer_setup(transport, &buf, buffer, sizeof(buffer));
+
do {
size_t parsed;
@@ -603,14 +605,15 @@ on_error:
static int http_close(git_transport *transport)
{
- transport_http *t = (transport_http *) transport;
+ if (gitno_ssl_teardown(transport) < 0)
+ return -1;
- if (gitno_close(t->socket) < 0) {
+ if (gitno_close(transport->socket) < 0) {
giterr_set(GITERR_OS, "Failed to close the socket: %s", strerror(errno));
return -1;
}
- t->parent.connected = 0;
+ transport->connected = 0;
return 0;
}
@@ -682,3 +685,23 @@ int git_transport_http(git_transport **out)
*out = (git_transport *) t;
return 0;
}
+
+int git_transport_https(git_transport **out)
+{
+#ifdef GIT_SSL
+ transport_http *t;
+ if (git_transport_http((git_transport **)&t) < 0)
+ return -1;
+
+ t->parent.encrypt = 1;
+ t->parent.check_cert = 1;
+ *out = (git_transport *) t;
+
+ return 0;
+#else
+ GIT_UNUSED(out);
+
+ giterr_set(GITERR_NET, "HTTPS support not available");
+ return -1;
+#endif
+}
diff --git a/src/util.c b/src/util.c
index ce770203a..3093cd767 100644
--- a/src/util.c
+++ b/src/util.c
@@ -179,7 +179,7 @@ void git__strtolower(char *str)
int git__prefixcmp(const char *str, const char *prefix)
{
for (;;) {
- char p = *(prefix++), s;
+ unsigned char p = *(prefix++), s;
if (!p)
return 0;
if ((s = *(str++)) != p)
diff --git a/src/win32/msvc-compat.h b/src/win32/msvc-compat.h
index 3ef09c85b..ccc091cd0 100644
--- a/src/win32/msvc-compat.h
+++ b/src/win32/msvc-compat.h
@@ -21,6 +21,7 @@
/* stat: file mode type testing macros */
# define _S_IFLNK 0120000
# define S_IFLNK _S_IFLNK
+# define S_IXUSR 00100
# define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR)
# define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG)
diff --git a/tests-clar/core/errors.c b/tests-clar/core/errors.c
index 0be3e7aca..10c0cdd3f 100644
--- a/tests-clar/core/errors.c
+++ b/tests-clar/core/errors.c
@@ -31,7 +31,7 @@ void test_core_errors__new_school(void)
do {
struct stat st;
memset(&st, 0, sizeof(st));
- assert(p_lstat("this_file_does_not_exist", &st) < 0);
+ cl_assert(p_lstat("this_file_does_not_exist", &st) < 0);
GIT_UNUSED(st);
} while (false);
giterr_set(GITERR_OS, "stat failed"); /* internal fn */
diff --git a/tests-clar/diff/iterator.c b/tests-clar/diff/iterator.c
index be29bea66..eee84810a 100644
--- a/tests-clar/diff/iterator.c
+++ b/tests-clar/diff/iterator.c
@@ -474,13 +474,14 @@ static const char *status_paths[] = {
"subdir/current_file",
"subdir/modified_file",
"subdir/new_file",
+ "\xe8\xbf\x99",
NULL
};
void test_diff_iterator__workdir_1(void)
{
workdir_iterator_test(
- "status", NULL, NULL, 12, 1, status_paths, "ignored_file");
+ "status", NULL, NULL, 13, 1, status_paths, "ignored_file");
}
static const char *status_paths_range_0[] = {
@@ -527,13 +528,14 @@ static const char *status_paths_range_4[] = {
"subdir/current_file",
"subdir/modified_file",
"subdir/new_file",
+ "\xe8\xbf\x99",
NULL
};
void test_diff_iterator__workdir_1_ranged_4(void)
{
workdir_iterator_test(
- "status", "subdir/", NULL, 3, 0, status_paths_range_4, NULL);
+ "status", "subdir/", NULL, 4, 0, status_paths_range_4, NULL);
}
static const char *status_paths_range_5[] = {
@@ -551,7 +553,7 @@ void test_diff_iterator__workdir_1_ranged_5(void)
void test_diff_iterator__workdir_1_ranged_empty_0(void)
{
workdir_iterator_test(
- "status", "z_does_not_exist", NULL,
+ "status", "\xff_does_not_exist", NULL,
0, 0, NULL, NULL);
}
diff --git a/tests-clar/diff/workdir.c b/tests-clar/diff/workdir.c
index 1ea1af86a..42152f1ad 100644
--- a/tests-clar/diff/workdir.c
+++ b/tests-clar/diff/workdir.c
@@ -37,12 +37,12 @@ void test_diff_workdir__to_index(void)
* - git diff
* - mv .git .gitted
*/
- cl_assert_equal_i(12, exp.files);
+ cl_assert_equal_i(13, exp.files);
cl_assert_equal_i(0, exp.file_adds);
cl_assert_equal_i(4, exp.file_dels);
cl_assert_equal_i(4, exp.file_mods);
cl_assert_equal_i(1, exp.file_ignored);
- cl_assert_equal_i(3, exp.file_untracked);
+ cl_assert_equal_i(4, exp.file_untracked);
cl_assert_equal_i(8, exp.hunks);
@@ -87,12 +87,12 @@ void test_diff_workdir__to_tree(void)
cl_git_pass(git_diff_foreach(
diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
- cl_assert(exp.files == 13);
+ cl_assert(exp.files == 14);
cl_assert(exp.file_adds == 0);
cl_assert(exp.file_dels == 4);
cl_assert(exp.file_mods == 4);
cl_assert(exp.file_ignored == 1);
- cl_assert(exp.file_untracked == 4);
+ cl_assert(exp.file_untracked == 5);
/* Since there is no git diff equivalent, let's just assume that the
* text diffs produced by git_diff_foreach are accurate here. We will
@@ -115,12 +115,12 @@ void test_diff_workdir__to_tree(void)
cl_git_pass(git_diff_foreach(
diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
- cl_assert(exp.files == 14);
+ cl_assert(exp.files == 15);
cl_assert(exp.file_adds == 2);
cl_assert(exp.file_dels == 5);
cl_assert(exp.file_mods == 4);
cl_assert(exp.file_ignored == 1);
- cl_assert(exp.file_untracked == 2);
+ cl_assert(exp.file_untracked == 3);
cl_assert(exp.hunks == 11);
@@ -144,12 +144,12 @@ void test_diff_workdir__to_tree(void)
cl_git_pass(git_diff_foreach(
diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
- cl_assert(exp.files == 15);
+ cl_assert(exp.files == 16);
cl_assert(exp.file_adds == 5);
cl_assert(exp.file_dels == 4);
cl_assert(exp.file_mods == 3);
cl_assert(exp.file_ignored == 1);
- cl_assert(exp.file_untracked == 2);
+ cl_assert(exp.file_untracked == 3);
cl_assert(exp.hunks == 12);
@@ -182,12 +182,12 @@ void test_diff_workdir__to_index_with_pathspec(void)
cl_git_pass(git_diff_workdir_to_index(g_repo, &opts, &diff));
cl_git_pass(git_diff_foreach(diff, &exp, diff_file_fn, NULL, NULL));
- cl_assert_equal_i(12, exp.files);
+ cl_assert_equal_i(13, exp.files);
cl_assert_equal_i(0, exp.file_adds);
cl_assert_equal_i(4, exp.file_dels);
cl_assert_equal_i(4, exp.file_mods);
cl_assert_equal_i(1, exp.file_ignored);
- cl_assert_equal_i(3, exp.file_untracked);
+ cl_assert_equal_i(4, exp.file_untracked);
git_diff_list_free(diff);
diff --git a/tests-clar/network/remotes.c b/tests-clar/network/remotes.c
index 3f631835c..eb7947dfb 100644
--- a/tests-clar/network/remotes.c
+++ b/tests-clar/network/remotes.c
@@ -167,7 +167,7 @@ void test_network_remotes__loading_a_missing_remote_returns_ENOTFOUND(void)
* [...]
* [remote "addtest"]
* url = http://github.com/libgit2/libgit2
- * fetch = +refs/heads/*:refs/remotes/addtest/*
+ * fetch = +refs/heads/\*:refs/remotes/addtest/\*
*/
void test_network_remotes__add(void)
{
diff --git a/tests-clar/object/blob/fromchunks.c b/tests-clar/object/blob/fromchunks.c
new file mode 100644
index 000000000..228e969b6
--- /dev/null
+++ b/tests-clar/object/blob/fromchunks.c
@@ -0,0 +1,87 @@
+#include "clar_libgit2.h"
+#include "buffer.h"
+#include "posix.h"
+#include "path.h"
+#include "fileops.h"
+
+static git_repository *repo;
+static char textual_content[] = "libgit2\n\r\n\0";
+
+void test_object_blob_fromchunks__initialize(void)
+{
+ repo = cl_git_sandbox_init("testrepo.git");
+}
+
+void test_object_blob_fromchunks__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+static int text_chunked_source_cb(char *content, size_t max_length, void *payload)
+{
+ int *count;
+
+ GIT_UNUSED(max_length);
+
+ count = (int *)payload;
+ (*count)--;
+
+ if (*count == 0)
+ return 0;
+
+ strcpy(content, textual_content);
+ return strlen(textual_content);
+}
+
+void test_object_blob_fromchunks__can_create_a_blob_from_a_in_memory_chunk_provider(void)
+{
+ git_oid expected_oid, oid;
+ git_object *blob;
+ int howmany = 7;
+
+ cl_git_pass(git_oid_fromstr(&expected_oid, "321cbdf08803c744082332332838df6bd160f8f9"));
+
+ cl_git_fail(git_object_lookup(&blob, repo, &expected_oid, GIT_OBJ_ANY));
+
+ cl_git_pass(git_blob_create_fromchunks(&oid, repo, NULL, text_chunked_source_cb, &howmany));
+
+ cl_git_pass(git_object_lookup(&blob, repo, &expected_oid, GIT_OBJ_ANY));
+ git_object_free(blob);
+}
+
+#define GITATTR "* text=auto\n" \
+ "*.txt text\n" \
+ "*.data binary\n"
+
+static void write_attributes(git_repository *repo)
+{
+ git_buf buf = GIT_BUF_INIT;
+
+ cl_git_pass(git_buf_joinpath(&buf, git_repository_path(repo), "info"));
+ cl_git_pass(git_buf_joinpath(&buf, git_buf_cstr(&buf), "attributes"));
+
+ cl_git_pass(git_futils_mkpath2file(git_buf_cstr(&buf), 0777));
+ cl_git_rewritefile(git_buf_cstr(&buf), GITATTR);
+
+ git_buf_free(&buf);
+}
+
+static void assert_named_chunked_blob(const char *expected_sha, const char *fake_name)
+{
+ git_oid expected_oid, oid;
+ int howmany = 7;
+
+ cl_git_pass(git_oid_fromstr(&expected_oid, expected_sha));
+
+ cl_git_pass(git_blob_create_fromchunks(&oid, repo, fake_name, text_chunked_source_cb, &howmany));
+ cl_assert(git_oid_cmp(&expected_oid, &oid) == 0);
+}
+
+void test_object_blob_fromchunks__creating_a_blob_from_chunks_honors_the_attributes_directives(void)
+{
+ write_attributes(repo);
+
+ assert_named_chunked_blob("321cbdf08803c744082332332838df6bd160f8f9", "dummy.data");
+ assert_named_chunked_blob("e9671e138a780833cb689753570fd10a55be84fb", "dummy.txt");
+ assert_named_chunked_blob("e9671e138a780833cb689753570fd10a55be84fb", "dummy.dunno");
+}
diff --git a/tests-clar/repo/init.c b/tests-clar/repo/init.c
index 7f16b5b7c..af54b2266 100644
--- a/tests-clar/repo/init.c
+++ b/tests-clar/repo/init.c
@@ -165,3 +165,71 @@ void test_repo_init__additional_templates(void)
git_buf_free(&path);
}
+
+static void assert_config_entry_on_init(const char *config_key, int expected_value)
+{
+ git_config *config;
+ int current_value;
+
+ cl_set_cleanup(&cleanup_repository, "config_entry");
+
+ cl_git_pass(git_repository_init(&_repo, "config_entry/test.git", 1));
+ git_repository_config(&config, _repo);
+
+ if (expected_value >= 0) {
+ cl_git_pass(git_config_get_bool(&current_value, config, config_key));
+
+ cl_assert_equal_i(expected_value, current_value);
+ } else {
+ int error = git_config_get_bool(&current_value, config, config_key);
+
+ cl_assert_equal_i(expected_value, error);
+ }
+
+ git_config_free(config);
+}
+
+void test_repo_init__detect_filemode(void)
+{
+#ifdef GIT_WIN32
+ assert_config_entry_on_init("core.filemode", false);
+#else
+ assert_config_entry_on_init("core.filemode", true);
+#endif
+}
+
+#define CASE_INSENSITIVE_FILESYSTEM (defined GIT_WIN32 || defined __APPLE__)
+
+void test_repo_init__detect_ignorecase(void)
+{
+#if CASE_INSENSITIVE_FILESYSTEM
+ assert_config_entry_on_init("core.ignorecase", true);
+#else
+ assert_config_entry_on_init("core.ignorecase", GIT_ENOTFOUND);
+#endif
+}
+
+void test_repo_init__reinit_doesnot_overwrite_ignorecase(void)
+{
+ git_config *config;
+ int current_value;
+
+ /* Init a new repo */
+ test_repo_init__detect_ignorecase();
+
+ /* Change the "core.ignorecase" config value to something unlikely */
+ git_repository_config(&config, _repo);
+ git_config_set_int32(config, "core.ignorecase", 42);
+ git_config_free(config);
+ git_repository_free(_repo);
+
+ /* Reinit the repository */
+ cl_git_pass(git_repository_init(&_repo, "config_entry/test.git", 1));
+ git_repository_config(&config, _repo);
+
+ /* Ensure the "core.ignorecase" config value hasn't been updated */
+ cl_git_pass(git_config_get_int32(&current_value, config, "core.ignorecase"));
+ cl_assert_equal_i(42, current_value);
+
+ git_config_free(config);
+}
diff --git a/tests-clar/resources/status/这 b/tests-clar/resources/status/这
new file mode 100644
index 000000000..f0ff9a197
--- /dev/null
+++ b/tests-clar/resources/status/这
@@ -0,0 +1 @@
+This
diff --git a/tests-clar/status/status_data.h b/tests-clar/status/status_data.h
index f109717e8..043b83009 100644
--- a/tests-clar/status/status_data.h
+++ b/tests-clar/status/status_data.h
@@ -19,6 +19,8 @@ static const char *entry_paths0[] = {
"subdir/deleted_file",
"subdir/modified_file",
"subdir/new_file",
+
+ "\xe8\xbf\x99",
};
static const unsigned int entry_statuses0[] = {
@@ -38,9 +40,11 @@ static const unsigned int entry_statuses0[] = {
GIT_STATUS_WT_DELETED,
GIT_STATUS_WT_MODIFIED,
GIT_STATUS_WT_NEW,
+
+ GIT_STATUS_WT_NEW,
};
-static const size_t entry_count0 = 15;
+static const size_t entry_count0 = 16;
/* entries for a copy of tests/resources/status with all content
* deleted from the working directory
@@ -108,6 +112,7 @@ static const char *entry_paths3[] = {
"subdir/current_file",
"subdir/deleted_file",
"subdir/modified_file",
+ "\xe8\xbf\x99",
};
static const unsigned int entry_statuses3[] = {
@@ -132,9 +137,10 @@ static const unsigned int entry_statuses3[] = {
GIT_STATUS_WT_DELETED,
GIT_STATUS_WT_DELETED,
GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_NEW,
};
-static const size_t entry_count3 = 21;
+static const size_t entry_count3 = 22;
/* entries for a copy of tests/resources/status with some mods
@@ -163,7 +169,8 @@ static const char *entry_paths4[] = {
"subdir/deleted_file",
"subdir/modified_file",
"zzz_new_dir/new_file",
- "zzz_new_file"
+ "zzz_new_file",
+ "\xe8\xbf\x99",
};
static const unsigned int entry_statuses4[] = {
@@ -189,6 +196,7 @@ static const unsigned int entry_statuses4[] = {
GIT_STATUS_WT_DELETED,
GIT_STATUS_WT_NEW,
GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_NEW,
};
-static const size_t entry_count4 = 22;
+static const size_t entry_count4 = 23;