summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/attr_file.c9
-rw-r--r--src/checkout.c2
-rw-r--r--src/config.c5
-rw-r--r--src/config_file.c165
-rw-r--r--src/diff_tform.c10
-rw-r--r--src/filter.c6
-rw-r--r--src/global.c32
-rw-r--r--src/global.h4
-rw-r--r--src/index.c18
-rw-r--r--src/netops.h4
-rw-r--r--src/openssl_stream.c6
-rw-r--r--src/rebase.c258
-rw-r--r--src/reflog.c2
-rw-r--r--src/remote.c8
-rw-r--r--src/reset.c6
-rw-r--r--src/settings.c6
-rw-r--r--src/stransport_stream.c249
-rw-r--r--src/stransport_stream.h14
-rw-r--r--src/tls_stream.c28
-rw-r--r--src/tls_stream.h21
-rw-r--r--src/transport.c2
-rw-r--r--src/transports/http.c6
-rw-r--r--src/win32/pthread.c2
23 files changed, 622 insertions, 241 deletions
diff --git a/src/attr_file.c b/src/attr_file.c
index 8997946b9..eed39661f 100644
--- a/src/attr_file.c
+++ b/src/attr_file.c
@@ -394,6 +394,7 @@ bool git_attr_fnmatch__match(
if ((match->flags & GIT_ATTR_FNMATCH_DIRECTORY) && !path->is_dir) {
int matchval;
+ char *matchpath;
/* for attribute checks or root ignore checks, fail match */
if (!(match->flags & GIT_ATTR_FNMATCH_IGNORE) ||
@@ -403,7 +404,13 @@ bool git_attr_fnmatch__match(
/* for ignore checks, use container of current item for check */
path->basename[-1] = '\0';
flags |= FNM_LEADING_DIR;
- matchval = p_fnmatch(match->pattern, path->path, flags);
+
+ if (match->containing_dir)
+ matchpath = path->basename;
+ else
+ matchpath = path->path;
+
+ matchval = p_fnmatch(match->pattern, matchpath, flags);
path->basename[-1] = '/';
return (matchval != FNM_NOMATCH);
}
diff --git a/src/checkout.c b/src/checkout.c
index 0b6e298a0..478130879 100644
--- a/src/checkout.c
+++ b/src/checkout.c
@@ -1457,7 +1457,7 @@ static int blob_content_to_file(
writer.fd = fd;
writer.open = 1;
- error = git_filter_list_stream_blob(fl, blob, (git_writestream *)&writer);
+ error = git_filter_list_stream_blob(fl, blob, &writer.base);
assert(writer.open == 0);
diff --git a/src/config.c b/src/config.c
index 550b22723..1400b9513 100644
--- a/src/config.c
+++ b/src/config.c
@@ -343,7 +343,6 @@ typedef struct {
git_config_iterator *current;
const git_config *cfg;
regex_t regex;
- int has_regex;
size_t i;
} all_iter;
@@ -480,7 +479,6 @@ int git_config_iterator_glob_new(git_config_iterator **out, const git_config *cf
if ((result = regcomp(&iter->regex, regexp, REG_EXTENDED)) != 0) {
giterr_set_regex(&iter->regex, result);
- regfree(&iter->regex);
git__free(iter);
return -1;
}
@@ -983,7 +981,8 @@ void multivar_iter_free(git_config_iterator *_iter)
iter->iter->free(iter->iter);
git__free(iter->name);
- regfree(&iter->regex);
+ if (iter->have_regex)
+ regfree(&iter->regex);
git__free(iter);
}
diff --git a/src/config_file.c b/src/config_file.c
index 8a2c88cd2..010c494eb 100644
--- a/src/config_file.c
+++ b/src/config_file.c
@@ -1162,17 +1162,24 @@ static int skip_bom(struct reader *reader)
static int strip_comments(char *line, int in_quotes)
{
- int quote_count = in_quotes;
+ int quote_count = in_quotes, backslash_count = 0;
char *ptr;
for (ptr = line; *ptr; ++ptr) {
if (ptr[0] == '"' && ptr > line && ptr[-1] != '\\')
quote_count++;
- if ((ptr[0] == ';' || ptr[0] == '#') && (quote_count % 2) == 0) {
+ if ((ptr[0] == ';' || ptr[0] == '#') &&
+ (quote_count % 2) == 0 &&
+ (backslash_count % 2) == 0) {
ptr[0] = '\0';
break;
}
+
+ if (ptr[0] == '\\')
+ backslash_count++;
+ else
+ backslash_count = 0;
}
/* skip any space at the end */
@@ -1416,7 +1423,7 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
while (!reader->eof) {
c = reader_peek(reader, SKIP_WHITESPACE);
- if (c == '\0') { /* We've arrived at the end of the file */
+ if (c == '\n') { /* We've arrived at the end of the file */
break;
} else if (c == '[') { /* section header, new section begins */
@@ -1453,9 +1460,12 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
* don't loose that information, but we only need to
* update post_start if we're going to use it in this
* iteration.
+ * If the section doesn't match and we are trying to delete an entry
+ * (value == NULL), we must continue searching; there may be another
+ * matching section later.
*/
if (!section_matches) {
- if (!last_section_matched) {
+ if (!last_section_matched || value == NULL) {
reader_consume_line(reader);
continue;
}
@@ -1619,76 +1629,69 @@ static char *escape_value(const char *ptr)
}
/* '\"' -> '"' etc */
-static char *fixup_line(const char *ptr, int quote_count)
+static int unescape_line(
+ char **out, bool *is_multi, const char *ptr, int quote_count)
{
- char *str, *out, *esc;
+ char *str, *fixed, *esc;
size_t ptr_len = strlen(ptr), alloc_len;
+ *is_multi = false;
+
if (GIT_ADD_SIZET_OVERFLOW(&alloc_len, ptr_len, 1) ||
(str = git__malloc(alloc_len)) == NULL) {
- return NULL;
+ return -1;
}
- out = str;
+ fixed = str;
while (*ptr != '\0') {
if (*ptr == '"') {
quote_count++;
} else if (*ptr != '\\') {
- *out++ = *ptr;
+ *fixed++ = *ptr;
} else {
/* backslash, check the next char */
ptr++;
/* if we're at the end, it's a multiline, so keep the backslash */
if (*ptr == '\0') {
- *out++ = '\\';
- goto out;
+ *is_multi = true;
+ goto done;
}
if ((esc = strchr(escapes, *ptr)) != NULL) {
- *out++ = escaped[esc - escapes];
+ *fixed++ = escaped[esc - escapes];
} else {
git__free(str);
giterr_set(GITERR_CONFIG, "Invalid escape at %s", ptr);
- return NULL;
+ return -1;
}
}
ptr++;
}
-out:
- *out = '\0';
+done:
+ *fixed = '\0';
+ *out = str;
- return str;
-}
-
-static int is_multiline_var(const char *str)
-{
- int count = 0;
- const char *end = str + strlen(str);
- while (end > str && end[-1] == '\\') {
- count++;
- end--;
- }
-
- /* An odd number means last backslash wasn't escaped, so it's multiline */
- return count & 1;
+ return 0;
}
static int parse_multiline_variable(struct reader *reader, git_buf *value, int in_quotes)
{
char *line = NULL, *proc_line = NULL;
int quote_count;
+ bool multiline;
/* Check that the next line exists */
line = reader_readline(reader, false);
if (line == NULL)
return -1;
- /* We've reached the end of the file, there is input missing */
+ /* We've reached the end of the file, there is no continuation.
+ * (this is not an error).
+ */
if (line[0] == '\0') {
- set_parse_error(reader, 0, "Unexpected end of file while parsing multine var");
git__free(line);
- return -1;
+ return 0;
}
quote_count = strip_comments(line, !!in_quotes);
@@ -1700,18 +1703,12 @@ static int parse_multiline_variable(struct reader *reader, git_buf *value, int i
/* TODO: unbounded recursion. This **could** be exploitable */
}
- /* Drop the continuation character '\': to closely follow the UNIX
- * standard, this character **has** to be last one in the buf, with
- * no whitespace after it */
- assert(is_multiline_var(value->ptr));
- git_buf_shorten(value, 1);
-
- proc_line = fixup_line(line, in_quotes);
- if (proc_line == NULL) {
+ if (unescape_line(&proc_line, &multiline, line, in_quotes) < 0) {
git__free(line);
return -1;
}
/* add this line to the multiline var */
+
git_buf_puts(value, proc_line);
git__free(line);
git__free(proc_line);
@@ -1720,18 +1717,57 @@ static int parse_multiline_variable(struct reader *reader, git_buf *value, int i
* If we need to continue reading the next line, let's just
* keep putting stuff in the buffer
*/
- if (is_multiline_var(value->ptr))
+ if (multiline)
return parse_multiline_variable(reader, value, quote_count);
return 0;
}
+GIT_INLINE(bool) is_namechar(char c)
+{
+ return isalnum(c) || c == '-';
+}
+
+static int parse_name(
+ char **name, const char **value, struct reader *reader, const char *line)
+{
+ const char *name_end = line, *value_start;
+
+ *name = NULL;
+ *value = NULL;
+
+ while (*name_end && is_namechar(*name_end))
+ name_end++;
+
+ if (line == name_end) {
+ set_parse_error(reader, 0, "Invalid configuration key");
+ return -1;
+ }
+
+ value_start = name_end;
+
+ while (*value_start && git__isspace(*value_start))
+ value_start++;
+
+ if (*value_start == '=') {
+ *value = value_start + 1;
+ } else if (*value_start) {
+ set_parse_error(reader, 0, "Invalid configuration key");
+ return -1;
+ }
+
+ if ((*name = git__strndup(line, name_end - line)) == NULL)
+ return -1;
+
+ return 0;
+}
+
static int parse_variable(struct reader *reader, char **var_name, char **var_value)
{
- const char *var_end = NULL;
const char *value_start = NULL;
char *line;
int quote_count;
+ bool multiline;
line = reader_readline(reader, true);
if (line == NULL)
@@ -1739,18 +1775,8 @@ static int parse_variable(struct reader *reader, char **var_name, char **var_val
quote_count = strip_comments(line, 0);
- var_end = strchr(line, '=');
-
- if (var_end == NULL)
- var_end = strchr(line, '\0');
- else
- value_start = var_end + 1;
-
- do var_end--;
- while (var_end>line && git__isspace(*var_end));
-
- *var_name = git__strndup(line, var_end - line + 1);
- GITERR_CHECK_ALLOC(*var_name);
+ if (parse_name(var_name, &value_start, reader, line) < 0)
+ return -1;
/* If there is no value, boolean true is assumed */
*var_value = NULL;
@@ -1762,31 +1788,28 @@ static int parse_variable(struct reader *reader, char **var_name, char **var_val
while (git__isspace(value_start[0]))
value_start++;
- if (is_multiline_var(value_start)) {
+ if (unescape_line(var_value, &multiline, value_start, 0) < 0)
+ goto on_error;
+
+ if (multiline) {
git_buf multi_value = GIT_BUF_INIT;
- char *proc_line = fixup_line(value_start, 0);
- GITERR_CHECK_ALLOC(proc_line);
- git_buf_puts(&multi_value, proc_line);
- git__free(proc_line);
- if (parse_multiline_variable(reader, &multi_value, quote_count) < 0 || git_buf_oom(&multi_value)) {
- git__free(*var_name);
- git__free(line);
+ git_buf_attach(&multi_value, *var_value, 0);
+
+ if (parse_multiline_variable(reader, &multi_value, quote_count) < 0 ||
+ git_buf_oom(&multi_value)) {
git_buf_free(&multi_value);
- return -1;
+ goto on_error;
}
*var_value = git_buf_detach(&multi_value);
-
- }
- else if (value_start[0] != '\0') {
- *var_value = fixup_line(value_start, 0);
- GITERR_CHECK_ALLOC(*var_value);
- } else { /* equals sign but missing rhs */
- *var_value = git__strdup("");
- GITERR_CHECK_ALLOC(*var_value);
}
}
git__free(line);
return 0;
+
+on_error:
+ git__free(*var_name);
+ git__free(line);
+ return -1;
}
diff --git a/src/diff_tform.c b/src/diff_tform.c
index 06c12308b..7d8463573 100644
--- a/src/diff_tform.c
+++ b/src/diff_tform.c
@@ -84,11 +84,14 @@ static git_diff_delta *diff_delta__merge_like_cgit(
* index (i.e. not in HEAD nor workdir) is given as empty.
*/
if (dup->status == GIT_DELTA_DELETED) {
- if (a->status == GIT_DELTA_ADDED)
+ if (a->status == GIT_DELTA_ADDED) {
dup->status = GIT_DELTA_UNMODIFIED;
+ dup->nfiles = 2;
+ }
/* else don't overwrite DELETE status */
} else {
dup->status = a->status;
+ dup->nfiles = a->nfiles;
}
git_oid_cpy(&dup->old_file.id, &a->old_file.id);
@@ -118,10 +121,13 @@ static git_diff_delta *diff_delta__merge_like_cgit_reversed(
return dup;
if (dup->status == GIT_DELTA_DELETED) {
- if (b->status == GIT_DELTA_ADDED)
+ if (b->status == GIT_DELTA_ADDED) {
dup->status = GIT_DELTA_UNMODIFIED;
+ dup->nfiles = 2;
+ }
} else {
dup->status = b->status;
+ dup->nfiles = b->nfiles;
}
git_oid_cpy(&dup->old_file.id, &b->old_file.id);
diff --git a/src/filter.c b/src/filter.c
index 8bd7f4d07..19c793e89 100644
--- a/src/filter.c
+++ b/src/filter.c
@@ -671,7 +671,7 @@ int git_filter_list_apply_to_data(
buf_stream_init(&writer, tgt);
if ((error = git_filter_list_stream_data(filters, src,
- (git_writestream *)&writer)) < 0)
+ &writer.parent)) < 0)
return error;
assert(writer.complete);
@@ -690,7 +690,7 @@ int git_filter_list_apply_to_file(
buf_stream_init(&writer, out);
if ((error = git_filter_list_stream_file(
- filters, repo, path, (git_writestream *)&writer)) < 0)
+ filters, repo, path, &writer.parent)) < 0)
return error;
assert(writer.complete);
@@ -721,7 +721,7 @@ int git_filter_list_apply_to_blob(
buf_stream_init(&writer, out);
if ((error = git_filter_list_stream_blob(
- filters, blob, (git_writestream *)&writer)) < 0)
+ filters, blob, &writer.parent)) < 0)
return error;
assert(writer.complete);
diff --git a/src/global.c b/src/global.c
index 1f3432d09..9f1a0bf10 100644
--- a/src/global.c
+++ b/src/global.c
@@ -17,7 +17,7 @@ git_mutex git__mwindow_mutex;
#define MAX_SHUTDOWN_CB 8
-#ifdef GIT_SSL
+#ifdef GIT_OPENSSL
# include <openssl/ssl.h>
SSL_CTX *git__ssl_ctx;
# ifdef GIT_THREADS
@@ -57,7 +57,7 @@ static void git__shutdown(void)
}
}
-#if defined(GIT_THREADS) && defined(GIT_SSL)
+#if defined(GIT_THREADS) && defined(GIT_OPENSSL)
void openssl_locking_function(int mode, int n, const char *file, int line)
{
int lock;
@@ -89,7 +89,7 @@ static void shutdown_ssl_locking(void)
static void init_ssl(void)
{
-#ifdef GIT_SSL
+#ifdef GIT_OPENSSL
long ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
/* Older OpenSSL and MacOS OpenSSL doesn't have this */
@@ -118,7 +118,7 @@ static void init_ssl(void)
int git_openssl_set_locking(void)
{
-#ifdef GIT_SSL
+#ifdef GIT_OPENSSL
# ifdef GIT_THREADS
int num_locks, i;
@@ -223,13 +223,10 @@ int git_libgit2_init(void)
static void synchronized_threads_shutdown(void)
{
- void *ptr;
-
/* Shut down any subsystems that have global state */
git__shutdown();
- ptr = TlsGetValue(_tls_index);
- git__global_state_cleanup(ptr);
+ git__free_tls_data();
TlsFree(_tls_index);
git_mutex_free(&git__mwindow_mutex);
@@ -270,15 +267,20 @@ git_global_st *git__global_state(void)
return ptr;
}
-BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, LPVOID reserved)
+/**
+ * Free the TLS data associated with this thread.
+ * This should only be used by the thread as it
+ * is exiting.
+ */
+void git__free_tls_data(void)
{
- if (reason == DLL_THREAD_DETACH) {
- void *ptr = TlsGetValue(_tls_index);
- git__global_state_cleanup(ptr);
- git__free(ptr);
- }
+ void *ptr = TlsGetValue(_tls_index);
+ if (!ptr)
+ return;
- return TRUE;
+ git__global_state_cleanup(ptr);
+ git__free(ptr);
+ TlsSetValue(_tls_index, NULL);
}
#elif defined(GIT_THREADS) && defined(_POSIX_THREADS)
diff --git a/src/global.h b/src/global.h
index a89a8d6ab..fdad6ba89 100644
--- a/src/global.h
+++ b/src/global.h
@@ -17,7 +17,7 @@ typedef struct {
char oid_fmt[GIT_OID_HEXSZ+1];
} git_global_st;
-#ifdef GIT_SSL
+#ifdef GIT_OPENSSL
# include <openssl/ssl.h>
extern SSL_CTX *git__ssl_ctx;
#endif
@@ -32,4 +32,6 @@ typedef void (*git_global_shutdown_fn)(void);
extern void git__on_shutdown(git_global_shutdown_fn callback);
+extern void git__free_tls_data(void);
+
#endif
diff --git a/src/index.c b/src/index.c
index dbcc37a58..0d2a03e72 100644
--- a/src/index.c
+++ b/src/index.c
@@ -987,9 +987,10 @@ static int index_no_dups(void **old, void *new)
* it, then it will return an error **and also free the entry**. When
* it replaces an existing entry, it will update the entry_ptr with the
* actual entry in the index (and free the passed in one).
+ * trust_mode is whether we trust the mode in entry_ptr.
*/
static int index_insert(
- git_index *index, git_index_entry **entry_ptr, int replace)
+ git_index *index, git_index_entry **entry_ptr, int replace, bool trust_mode)
{
int error = 0;
size_t path_length, position;
@@ -1021,7 +1022,10 @@ static int index_insert(
&position, index, entry->path, 0, GIT_IDXENTRY_STAGE(entry), false)) {
existing = index->entries.contents[position];
/* update filemode to existing values if stat is not trusted */
- entry->mode = index_merge_mode(index, existing, entry->mode);
+ if (trust_mode)
+ entry->mode = git_index__create_mode(entry->mode);
+ else
+ entry->mode = index_merge_mode(index, existing, entry->mode);
}
/* look for tree / blob name collisions, removing conflicts if requested */
@@ -1122,7 +1126,7 @@ int git_index_add_frombuffer(
git_oid_cpy(&entry->id, &id);
entry->file_size = len;
- if ((error = index_insert(index, &entry, 1)) < 0)
+ if ((error = index_insert(index, &entry, 1, true)) < 0)
return error;
/* Adding implies conflict was resolved, move conflict entries to REUC */
@@ -1142,7 +1146,7 @@ int git_index_add_bypath(git_index *index, const char *path)
assert(index && path);
if ((ret = index_entry_init(&entry, index, path)) < 0 ||
- (ret = index_insert(index, &entry, 1)) < 0)
+ (ret = index_insert(index, &entry, 1, false)) < 0)
return ret;
/* Adding implies conflict was resolved, move conflict entries to REUC */
@@ -1182,7 +1186,7 @@ int git_index_add(git_index *index, const git_index_entry *source_entry)
}
if ((ret = index_entry_dup(&entry, INDEX_OWNER(index), source_entry)) < 0 ||
- (ret = index_insert(index, &entry, 1)) < 0)
+ (ret = index_insert(index, &entry, 1, true)) < 0)
return ret;
git_tree_cache_invalidate_path(index->tree, entry->path);
@@ -1313,7 +1317,7 @@ int git_index_conflict_add(git_index *index,
/* Make sure stage is correct */
GIT_IDXENTRY_STAGE_SET(entries[i], i + 1);
- if ((ret = index_insert(index, &entries[i], 1)) < 0)
+ if ((ret = index_insert(index, &entries[i], 1, true)) < 0)
goto on_error;
entries[i] = NULL; /* don't free if later entry fails */
@@ -2537,7 +2541,7 @@ int git_index_add_all(
entry->id = blobid;
/* add working directory item to index */
- if ((error = index_insert(index, &entry, 1)) < 0)
+ if ((error = index_insert(index, &entry, 1, false)) < 0)
break;
git_tree_cache_invalidate_path(index->tree, wd->path);
diff --git a/src/netops.h b/src/netops.h
index d5f0ca3f3..b7170a0f2 100644
--- a/src/netops.h
+++ b/src/netops.h
@@ -11,12 +11,12 @@
#include "common.h"
#include "stream.h"
-#ifdef GIT_SSL
+#ifdef GIT_OPENSSL
# include <openssl/ssl.h>
#endif
typedef struct gitno_ssl {
-#ifdef GIT_SSL
+#ifdef GIT_OPENSSL
SSL *ssl;
#else
size_t dummy;
diff --git a/src/openssl_stream.c b/src/openssl_stream.c
index 9ddf6e4be..2ebfac738 100644
--- a/src/openssl_stream.c
+++ b/src/openssl_stream.c
@@ -5,7 +5,7 @@
* a Linking Exception. For full terms see the included COPYING file.
*/
-#ifdef GIT_SSL
+#ifdef GIT_OPENSSL
#include <ctype.h>
@@ -374,6 +374,10 @@ int git_openssl_stream_new(git_stream **out, const char *host, const char *port)
int git_openssl_stream_new(git_stream **out, const char *host, const char *port)
{
+ GIT_UNUSED(out);
+ GIT_UNUSED(host);
+ GIT_UNUSED(port);
+
giterr_set(GITERR_SSL, "openssl is not supported in this version");
return -1;
}
diff --git a/src/rebase.c b/src/rebase.c
index eb25d4c3a..b636e7951 100644
--- a/src/rebase.c
+++ b/src/rebase.c
@@ -57,6 +57,8 @@ typedef enum {
struct git_rebase {
git_repository *repo;
+ git_rebase_options options;
+
git_rebase_type_t type;
char *state_path;
@@ -249,7 +251,43 @@ done:
return error;
}
-int git_rebase_open(git_rebase **out, git_repository *repo)
+static git_rebase *rebase_alloc(const git_rebase_options *rebase_opts)
+{
+ git_rebase *rebase = git__calloc(1, sizeof(git_rebase));
+
+ if (!rebase)
+ return NULL;
+
+ if (rebase_opts)
+ memcpy(&rebase->options, rebase_opts, sizeof(git_rebase_options));
+ else
+ git_rebase_init_options(&rebase->options, GIT_REBASE_OPTIONS_VERSION);
+
+ if (rebase_opts && rebase_opts->rewrite_notes_ref) {
+ if ((rebase->options.rewrite_notes_ref = git__strdup(rebase_opts->rewrite_notes_ref)) == NULL)
+ return NULL;
+ }
+
+ if ((rebase->options.checkout_options.checkout_strategy & (GIT_CHECKOUT_SAFE | GIT_CHECKOUT_FORCE)) == 0)
+ rebase->options.checkout_options.checkout_strategy = GIT_CHECKOUT_SAFE;
+
+ return rebase;
+}
+
+static int rebase_check_versions(const git_rebase_options *given_opts)
+{
+ GITERR_CHECK_VERSION(given_opts, GIT_REBASE_OPTIONS_VERSION, "git_rebase_options");
+
+ if (given_opts)
+ GITERR_CHECK_VERSION(&given_opts->checkout_options, GIT_CHECKOUT_OPTIONS_VERSION, "git_checkout_options");
+
+ return 0;
+}
+
+int git_rebase_open(
+ git_rebase **out,
+ git_repository *repo,
+ const git_rebase_options *given_opts)
{
git_rebase *rebase;
git_buf path = GIT_BUF_INIT, orig_head_name = GIT_BUF_INIT,
@@ -258,7 +296,10 @@ int git_rebase_open(git_rebase **out, git_repository *repo)
assert(repo);
- rebase = git__calloc(1, sizeof(git_rebase));
+ if ((error = rebase_check_versions(given_opts)) < 0)
+ return error;
+
+ rebase = rebase_alloc(given_opts);
GITERR_CHECK_ALLOC(rebase);
rebase->repo = repo;
@@ -446,48 +487,6 @@ int git_rebase_init_options(git_rebase_options *opts, unsigned int version)
return 0;
}
-static int rebase_normalize_opts(
- git_repository *repo,
- git_rebase_options *opts,
- const git_rebase_options *given_opts)
-{
- git_rebase_options default_opts = GIT_REBASE_OPTIONS_INIT;
- git_config *config;
-
- if (given_opts)
- memcpy(opts, given_opts, sizeof(git_rebase_options));
- else
- memcpy(opts, &default_opts, sizeof(git_rebase_options));
-
- if (git_repository_config(&config, repo) < 0)
- return -1;
-
- if (given_opts && given_opts->rewrite_notes_ref) {
- opts->rewrite_notes_ref = git__strdup(given_opts->rewrite_notes_ref);
- GITERR_CHECK_ALLOC(opts->rewrite_notes_ref);
- } else if (git_config__get_bool_force(config, "notes.rewrite.rebase", 1)) {
- char *rewrite_ref = git_config__get_string_force(
- config, "notes.rewriteref", NOTES_DEFAULT_REF);
-
- if (rewrite_ref) {
- opts->rewrite_notes_ref = rewrite_ref;
- GITERR_CHECK_ALLOC(opts->rewrite_notes_ref);
- }
- }
-
- git_config_free(config);
-
- return 0;
-}
-
-static void rebase_opts_free(git_rebase_options *opts)
-{
- if (!opts)
- return;
-
- git__free((char *)opts->rewrite_notes_ref);
-}
-
static int rebase_ensure_not_in_progress(git_repository *repo)
{
int error;
@@ -504,33 +503,42 @@ static int rebase_ensure_not_in_progress(git_repository *repo)
return 0;
}
-static int rebase_ensure_not_dirty(git_repository *repo)
+static int rebase_ensure_not_dirty(
+ git_repository *repo,
+ bool check_index,
+ bool check_workdir,
+ int fail_with)
{
git_tree *head = NULL;
git_index *index = NULL;
git_diff *diff = NULL;
int error;
- if ((error = git_repository_head_tree(&head, repo)) < 0 ||
- (error = git_repository_index(&index, repo)) < 0 ||
- (error = git_diff_tree_to_index(&diff, repo, head, index, NULL)) < 0)
- goto done;
+ if (check_index) {
+ if ((error = git_repository_head_tree(&head, repo)) < 0 ||
+ (error = git_repository_index(&index, repo)) < 0 ||
+ (error = git_diff_tree_to_index(&diff, repo, head, index, NULL)) < 0)
+ goto done;
- if (git_diff_num_deltas(diff) > 0) {
- giterr_set(GITERR_REBASE, "Uncommitted changes exist in index");
- error = -1;
- goto done;
- }
+ if (git_diff_num_deltas(diff) > 0) {
+ giterr_set(GITERR_REBASE, "Uncommitted changes exist in index");
+ error = fail_with;
+ goto done;
+ }
- git_diff_free(diff);
- diff = NULL;
+ git_diff_free(diff);
+ diff = NULL;
+ }
- if ((error = git_diff_index_to_workdir(&diff, repo, index, NULL)) < 0)
- goto done;
+ if (check_workdir) {
+ if ((error = git_diff_index_to_workdir(&diff, repo, index, NULL)) < 0)
+ goto done;
- if (git_diff_num_deltas(diff) > 0) {
- giterr_set(GITERR_REBASE, "Unstaged changes exist in workdir");
- error = -1;
+ if (git_diff_num_deltas(diff) > 0) {
+ giterr_set(GITERR_REBASE, "Unstaged changes exist in workdir");
+ error = fail_with;
+ goto done;
+ }
}
done:
@@ -607,8 +615,7 @@ static int rebase_init(
git_repository *repo,
const git_annotated_commit *branch,
const git_annotated_commit *upstream,
- const git_annotated_commit *onto,
- const git_rebase_options *opts)
+ const git_annotated_commit *onto)
{
git_reference *head_ref = NULL;
git_annotated_commit *head_branch = NULL;
@@ -630,7 +637,7 @@ static int rebase_init(
rebase->type = GIT_REBASE_TYPE_MERGE;
rebase->state_path = git_buf_detach(&state_path);
rebase->orig_head_name = git__strdup(branch->ref_name ? branch->ref_name : ORIG_DETACHED_HEAD);
- rebase->quiet = opts->quiet;
+ rebase->quiet = rebase->options.quiet;
git_oid_cpy(&rebase->orig_head_id, git_annotated_commit_id(branch));
git_oid_cpy(&rebase->onto_id, git_annotated_commit_id(onto));
@@ -658,10 +665,8 @@ int git_rebase_init(
const git_rebase_options *given_opts)
{
git_rebase *rebase = NULL;
- git_rebase_options opts;
git_buf reflog = GIT_BUF_INIT;
git_commit *onto_commit = NULL;
- git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
git_reference *head_ref = NULL;
int error;
@@ -669,31 +674,26 @@ int git_rebase_init(
*out = NULL;
- GITERR_CHECK_VERSION(given_opts, GIT_REBASE_OPTIONS_VERSION, "git_rebase_options");
-
if (!onto)
onto = upstream;
- checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
-
- if ((error = rebase_normalize_opts(repo, &opts, given_opts)) < 0 ||
+ if ((error = rebase_check_versions(given_opts)) < 0 ||
(error = git_repository__ensure_not_bare(repo, "rebase")) < 0 ||
(error = rebase_ensure_not_in_progress(repo)) < 0 ||
- (error = rebase_ensure_not_dirty(repo)) < 0 ||
+ (error = rebase_ensure_not_dirty(repo, true, true, GIT_ERROR)) < 0 ||
(error = git_commit_lookup(
&onto_commit, repo, git_annotated_commit_id(onto))) < 0)
return error;
- rebase = git__calloc(1, sizeof(git_rebase));
- GITERR_CHECK_ALLOC(rebase);
+ rebase = rebase_alloc(given_opts);
if ((error = rebase_init(
- rebase, repo, branch, upstream, onto, &opts)) < 0 ||
+ rebase, repo, branch, upstream, onto)) < 0 ||
(error = rebase_setupfiles(rebase)) < 0 ||
(error = git_buf_printf(&reflog,
"rebase: checkout %s", rebase_onto_name(onto))) < 0 ||
(error = git_checkout_tree(
- repo, (git_object *)onto_commit, &checkout_opts)) < 0 ||
+ repo, (git_object *)onto_commit, &rebase->options.checkout_options)) < 0 ||
(error = git_reference_create(&head_ref, repo, GIT_HEAD_FILE,
git_annotated_commit_id(onto), 1, reflog.ptr)) < 0)
goto done;
@@ -709,25 +709,16 @@ done:
git_commit_free(onto_commit);
git_buf_free(&reflog);
- rebase_opts_free(&opts);
return error;
}
-static void normalize_checkout_opts(
- git_rebase *rebase,
- git_commit *current_commit,
+static void normalize_checkout_options_for_apply(
git_checkout_options *checkout_opts,
- const git_checkout_options *given_checkout_opts)
+ git_rebase *rebase,
+ git_commit *current_commit)
{
- if (given_checkout_opts != NULL)
- memcpy(checkout_opts, given_checkout_opts, sizeof(git_checkout_options));
- else {
- git_checkout_options default_checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
- default_checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
-
- memcpy(checkout_opts, &default_checkout_opts, sizeof(git_checkout_options));
- }
+ memcpy(checkout_opts, &rebase->options.checkout_options, sizeof(git_checkout_options));
if (!checkout_opts->ancestor_label)
checkout_opts->ancestor_label = "ancestor";
@@ -758,16 +749,15 @@ GIT_INLINE(int) rebase_movenext(git_rebase *rebase)
static int rebase_next_merge(
git_rebase_operation **out,
- git_rebase *rebase,
- git_checkout_options *given_checkout_opts)
+ git_rebase *rebase)
{
git_buf path = GIT_BUF_INIT;
- git_checkout_options checkout_opts = {0};
git_commit *current_commit = NULL, *parent_commit = NULL;
git_tree *current_tree = NULL, *head_tree = NULL, *parent_tree = NULL;
git_index *index = NULL;
git_indexwriter indexwriter = GIT_INDEXWRITER_INIT;
git_rebase_operation *operation;
+ git_checkout_options checkout_opts;
char current_idstr[GIT_OID_HEXSZ];
unsigned int parent_count;
int error;
@@ -796,7 +786,7 @@ static int rebase_next_merge(
git_oid_fmt(current_idstr, &operation->id);
- normalize_checkout_opts(rebase, current_commit, &checkout_opts, given_checkout_opts);
+ normalize_checkout_options_for_apply(&checkout_opts, rebase, current_commit);
if ((error = git_indexwriter_init_for_operation(&indexwriter, rebase->repo, &checkout_opts.checkout_strategy)) < 0 ||
(error = rebase_setupfile(rebase, MSGNUM_FILE, -1, "%d\n", rebase->current+1)) < 0 ||
@@ -824,8 +814,7 @@ done:
int git_rebase_next(
git_rebase_operation **out,
- git_rebase *rebase,
- git_checkout_options *checkout_opts)
+ git_rebase *rebase)
{
int error;
@@ -833,7 +822,7 @@ int git_rebase_next(
switch (rebase->type) {
case GIT_REBASE_TYPE_MERGE:
- error = rebase_next_merge(out, rebase, checkout_opts);
+ error = rebase_next_merge(out, rebase);
break;
default:
abort();
@@ -869,11 +858,12 @@ static int rebase_commit_merge(
if (git_index_has_conflicts(index)) {
giterr_set(GITERR_REBASE, "Conflicts have not been resolved");
- error = GIT_EMERGECONFLICT;
+ error = GIT_EUNMERGED;
goto done;
}
- if ((error = git_commit_lookup(&current_commit, rebase->repo, &operation->id)) < 0 ||
+ if ((error = rebase_ensure_not_dirty(rebase->repo, false, true, GIT_EUNMERGED)) < 0 ||
+ (error = git_commit_lookup(&current_commit, rebase->repo, &operation->id)) < 0 ||
(error = git_repository_head(&head, rebase->repo)) < 0 ||
(error = git_reference_peel((git_object **)&head_commit, head, GIT_OBJ_COMMIT)) < 0 ||
(error = git_commit_tree(&head_tree, head_commit)) < 0 ||
@@ -971,7 +961,7 @@ int git_rebase_abort(git_rebase *rebase)
if ((error = git_commit_lookup(
&orig_head_commit, rebase->repo, &rebase->orig_head_id)) < 0 ||
(error = git_reset(rebase->repo, (git_object *)orig_head_commit,
- GIT_RESET_HARD, NULL)) < 0)
+ GIT_RESET_HARD, &rebase->options.checkout_options)) < 0)
goto done;
error = rebase_cleanup(rebase);
@@ -983,19 +973,50 @@ done:
return error;
}
+static int notes_ref_lookup(git_buf *out, git_rebase *rebase)
+{
+ git_config *config = NULL;
+ int do_rewrite, error;
+
+ if (rebase->options.rewrite_notes_ref) {
+ git_buf_attach_notowned(out,
+ rebase->options.rewrite_notes_ref,
+ strlen(rebase->options.rewrite_notes_ref));
+ return 0;
+ }
+
+ if ((error = git_repository_config(&config, rebase->repo)) < 0 ||
+ (error = git_config_get_bool(&do_rewrite, config, "notes.rewrite.rebase")) < 0) {
+
+ if (error != GIT_ENOTFOUND)
+ goto done;
+
+ giterr_clear();
+ do_rewrite = 1;
+ }
+
+ error = do_rewrite ?
+ git_config_get_string_buf(out, config, "notes.rewriteref") :
+ GIT_ENOTFOUND;
+
+done:
+ git_config_free(config);
+ return error;
+}
+
static int rebase_copy_note(
git_rebase *rebase,
+ const char *notes_ref,
git_oid *from,
git_oid *to,
- const git_signature *committer,
- const git_rebase_options *opts)
+ const git_signature *committer)
{
git_note *note = NULL;
git_oid note_id;
git_signature *who = NULL;
int error;
- if ((error = git_note_read(&note, rebase->repo, opts->rewrite_notes_ref, from)) < 0) {
+ if ((error = git_note_read(&note, rebase->repo, notes_ref, from)) < 0) {
if (error == GIT_ENOTFOUND) {
giterr_clear();
error = 0;
@@ -1016,7 +1037,7 @@ static int rebase_copy_note(
committer = who;
}
- error = git_note_create(&note_id, rebase->repo, opts->rewrite_notes_ref,
+ error = git_note_create(&note_id, rebase->repo, notes_ref,
git_note_author(note), committer, to, git_note_message(note), 0);
done:
@@ -1028,17 +1049,22 @@ done:
static int rebase_copy_notes(
git_rebase *rebase,
- const git_signature *committer,
- const git_rebase_options *opts)
+ const git_signature *committer)
{
- git_buf path = GIT_BUF_INIT, rewritten = GIT_BUF_INIT;
+ git_buf path = GIT_BUF_INIT, rewritten = GIT_BUF_INIT, notes_ref = GIT_BUF_INIT;
char *pair_list, *fromstr, *tostr, *end;
git_oid from, to;
unsigned int linenum = 1;
int error = 0;
- if (!opts->rewrite_notes_ref)
+ if ((error = notes_ref_lookup(&notes_ref, rebase)) < 0) {
+ if (error == GIT_ENOTFOUND) {
+ giterr_clear();
+ error = 0;
+ }
+
goto done;
+ }
if ((error = git_buf_joinpath(&path, rebase->state_path, REWRITTEN_FILE)) < 0 ||
(error = git_futils_readbuffer(&rewritten, path.ptr)) < 0)
@@ -1067,7 +1093,7 @@ static int rebase_copy_notes(
git_oid_fromstr(&to, tostr) < 0)
goto on_error;
- if ((error = rebase_copy_note(rebase, &from, &to, committer, opts)) < 0)
+ if ((error = rebase_copy_note(rebase, notes_ref.ptr, &from, &to, committer)) < 0)
goto done;
linenum++;
@@ -1082,16 +1108,15 @@ on_error:
done:
git_buf_free(&rewritten);
git_buf_free(&path);
+ git_buf_free(&notes_ref);
return error;
}
int git_rebase_finish(
git_rebase *rebase,
- const git_signature *signature,
- const git_rebase_options *given_opts)
+ const git_signature *signature)
{
- git_rebase_options opts;
git_reference *terminal_ref = NULL, *branch_ref = NULL, *head_ref = NULL;
git_commit *terminal_commit = NULL;
git_buf branch_msg = GIT_BUF_INIT, head_msg = GIT_BUF_INIT;
@@ -1100,11 +1125,6 @@ int git_rebase_finish(
assert(rebase);
- GITERR_CHECK_VERSION(given_opts, GIT_REBASE_OPTIONS_VERSION, "git_rebase_options");
-
- if ((error = rebase_normalize_opts(rebase->repo, &opts, given_opts)) < 0)
- goto done;
-
git_oid_fmt(onto, &rebase->onto_id);
if ((error = git_buf_printf(&branch_msg, "rebase finished: %s onto %.*s",
@@ -1120,7 +1140,7 @@ int git_rebase_finish(
(error = git_reference_symbolic_create(&head_ref,
rebase->repo, GIT_HEAD_FILE, rebase->orig_head_name, 1,
head_msg.ptr)) < 0 ||
- (error = rebase_copy_notes(rebase, signature, &opts)) < 0)
+ (error = rebase_copy_notes(rebase, signature)) < 0)
goto done;
error = rebase_cleanup(rebase);
@@ -1132,7 +1152,6 @@ done:
git_reference_free(head_ref);
git_reference_free(branch_ref);
git_reference_free(terminal_ref);
- rebase_opts_free(&opts);
return error;
}
@@ -1148,7 +1167,7 @@ size_t git_rebase_operation_current(git_rebase *rebase)
{
assert(rebase);
- return rebase->current;
+ return rebase->started ? rebase->current : GIT_REBASE_NO_OPERATION;
}
git_rebase_operation *git_rebase_operation_byindex(git_rebase *rebase, size_t idx)
@@ -1167,5 +1186,6 @@ void git_rebase_free(git_rebase *rebase)
git__free(rebase->orig_head_name);
git__free(rebase->state_path);
git_array_clear(rebase->operations);
+ git__free((char *)rebase->options.rewrite_notes_ref);
git__free(rebase);
}
diff --git a/src/reflog.c b/src/reflog.c
index 22aa7d8ed..9ce9aee6f 100644
--- a/src/reflog.c
+++ b/src/reflog.c
@@ -193,7 +193,7 @@ int git_reflog_drop(git_reflog *reflog, size_t idx, int rewrite_previous_entry)
entry = (git_reflog_entry *)git_reflog_entry_byindex(reflog, idx);
if (entry == NULL) {
- giterr_set(GITERR_REFERENCE, "No reflog entry at index "PRIuZ, idx);
+ giterr_set(GITERR_REFERENCE, "No reflog entry at index %"PRIuZ, idx);
return GIT_ENOTFOUND;
}
diff --git a/src/remote.c b/src/remote.c
index 948060da6..5257e85f3 100644
--- a/src/remote.c
+++ b/src/remote.c
@@ -171,11 +171,11 @@ static int create_internal(git_remote **out, git_repository *repo, const char *n
if ((error = git_repository_config_snapshot(&config, repo)) < 0)
goto on_error;
- if (lookup_remote_prune_config(remote, config, name) < 0)
+ if ((error = lookup_remote_prune_config(remote, config, name)) < 0)
goto on_error;
/* Move the data over to where the matching functions can find them */
- if (dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs) < 0)
+ if ((error = dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs)) < 0)
goto on_error;
}
@@ -457,7 +457,7 @@ int git_remote_lookup(git_remote **out, git_repository *repo, const char *name)
goto cleanup;
/* Move the data over to where the matching functions can find them */
- if (dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs) < 0)
+ if ((error = dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs)) < 0)
goto cleanup;
*out = remote;
@@ -2330,7 +2330,7 @@ int git_remote_upload(git_remote *remote, const git_strarray *refspecs, const gi
goto cleanup;
free_refspecs(&remote->active_refspecs);
- if (dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs) < 0)
+ if ((error = dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs)) < 0)
goto cleanup;
if (remote->push) {
diff --git a/src/reset.c b/src/reset.c
index aaebf4198..d08e48cd7 100644
--- a/src/reset.c
+++ b/src/reset.c
@@ -102,7 +102,7 @@ static int reset(
git_object *target,
const char *to,
git_reset_t reset_type,
- git_checkout_options *checkout_opts)
+ const git_checkout_options *checkout_opts)
{
git_object *commit = NULL;
git_index *index = NULL;
@@ -183,7 +183,7 @@ int git_reset(
git_repository *repo,
git_object *target,
git_reset_t reset_type,
- git_checkout_options *checkout_opts)
+ const git_checkout_options *checkout_opts)
{
return reset(repo, target, git_oid_tostr_s(git_object_id(target)), reset_type, checkout_opts);
}
@@ -192,7 +192,7 @@ int git_reset_from_annotated(
git_repository *repo,
git_annotated_commit *commit,
git_reset_t reset_type,
- git_checkout_options *checkout_opts)
+ const git_checkout_options *checkout_opts)
{
return reset(repo, (git_object *) commit->commit, commit->ref_name, reset_type, checkout_opts);
}
diff --git a/src/settings.c b/src/settings.c
index 971b50935..2097ca314 100644
--- a/src/settings.c
+++ b/src/settings.c
@@ -5,7 +5,7 @@
* a Linking Exception. For full terms see the included COPYING file.
*/
-#ifdef GIT_SSL
+#ifdef GIT_OPENSSL
# include <openssl/err.h>
#endif
@@ -28,7 +28,7 @@ int git_libgit2_features()
#ifdef GIT_THREADS
| GIT_FEATURE_THREADS
#endif
-#if defined(GIT_SSL) || defined(GIT_WINHTTP)
+#if defined(GIT_OPENSSL) || defined(GIT_WINHTTP) || defined(GIT_SECURE_TRANSPORT)
| GIT_FEATURE_HTTPS
#endif
#if defined(GIT_SSH)
@@ -138,7 +138,7 @@ int git_libgit2_opts(int key, ...)
break;
case GIT_OPT_SET_SSL_CERT_LOCATIONS:
-#ifdef GIT_SSL
+#ifdef GIT_OPENSSL
{
const char *file = va_arg(ap, const char *);
const char *path = va_arg(ap, const char *);
diff --git a/src/stransport_stream.c b/src/stransport_stream.c
new file mode 100644
index 000000000..429aa2d55
--- /dev/null
+++ b/src/stransport_stream.c
@@ -0,0 +1,249 @@
+/*
+ * 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.
+ */
+
+#ifdef GIT_SECURE_TRANSPORT
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <Security/SecureTransport.h>
+#include <Security/SecCertificate.h>
+
+#include "git2/transport.h"
+
+#include "socket_stream.h"
+
+int stransport_error(OSStatus ret)
+{
+ CFStringRef message;
+
+ if (ret == noErr || ret == errSSLClosedGraceful) {
+ giterr_clear();
+ return 0;
+ }
+
+ message = SecCopyErrorMessageString(ret, NULL);
+ GITERR_CHECK_ALLOC(message);
+
+ giterr_set(GITERR_NET, "SecureTransport error: %s", CFStringGetCStringPtr(message, kCFStringEncodingUTF8));
+ CFRelease(message);
+ return -1;
+}
+
+typedef struct {
+ git_stream parent;
+ git_stream *io;
+ SSLContextRef ctx;
+ CFDataRef der_data;
+ git_cert_x509 cert_info;
+} stransport_stream;
+
+int stransport_connect(git_stream *stream)
+{
+ stransport_stream *st = (stransport_stream *) stream;
+ int error;
+ SecTrustRef trust = NULL;
+ SecTrustResultType sec_res;
+ OSStatus ret;
+
+ if ((error = git_stream_connect(st->io)) < 0)
+ return error;
+
+ ret = SSLHandshake(st->ctx);
+ if (ret != errSSLServerAuthCompleted) {
+ giterr_set(GITERR_SSL, "unexpected return value from ssl handshake %d", ret);
+ return -1;
+ }
+
+ if ((ret = SSLCopyPeerTrust(st->ctx, &trust)) != noErr)
+ goto on_error;
+
+ if ((ret = SecTrustEvaluate(trust, &sec_res)) != noErr)
+ goto on_error;
+
+ CFRelease(trust);
+
+ if (sec_res == kSecTrustResultInvalid || sec_res == kSecTrustResultOtherError) {
+ giterr_set(GITERR_SSL, "internal security trust error");
+ return -1;
+ }
+
+ if (sec_res == kSecTrustResultDeny || sec_res == kSecTrustResultRecoverableTrustFailure ||
+ sec_res == kSecTrustResultFatalTrustFailure)
+ return GIT_ECERTIFICATE;
+
+ return 0;
+
+on_error:
+ if (trust)
+ CFRelease(trust);
+
+ return stransport_error(ret);
+}
+
+int stransport_certificate(git_cert **out, git_stream *stream)
+{
+ stransport_stream *st = (stransport_stream *) stream;
+ SecTrustRef trust = NULL;
+ SecCertificateRef sec_cert;
+ OSStatus ret;
+
+ if ((ret = SSLCopyPeerTrust(st->ctx, &trust)) != noErr)
+ return stransport_error(ret);
+
+ sec_cert = SecTrustGetCertificateAtIndex(trust, 0);
+ st->der_data = SecCertificateCopyData(sec_cert);
+ CFRelease(trust);
+
+ if (st->der_data == NULL) {
+ giterr_set(GITERR_SSL, "retrieved invalid certificate data");
+ return -1;
+ }
+
+ st->cert_info.cert_type = GIT_CERT_X509;
+ st->cert_info.data = (void *) CFDataGetBytePtr(st->der_data);
+ st->cert_info.len = CFDataGetLength(st->der_data);
+
+ *out = (git_cert *)&st->cert_info;
+ return 0;
+}
+
+static OSStatus write_cb(SSLConnectionRef conn, const void *data, size_t *len)
+{
+ git_stream *io = (git_stream *) conn;
+ ssize_t ret;
+
+ ret = git_stream_write(io, data, *len, 0);
+ if (ret < 0) {
+ *len = 0;
+ return -1;
+ }
+
+ *len = ret;
+
+ return noErr;
+}
+
+ssize_t stransport_write(git_stream *stream, const char *data, size_t len, int flags)
+{
+ stransport_stream *st = (stransport_stream *) stream;
+ size_t data_len, processed;
+ OSStatus ret;
+
+ GIT_UNUSED(flags);
+
+ data_len = len;
+ if ((ret = SSLWrite(st->ctx, data, data_len, &processed)) != noErr)
+ return stransport_error(ret);
+
+ return processed;
+}
+
+static OSStatus read_cb(SSLConnectionRef conn, void *data, size_t *len)
+{
+ git_stream *io = (git_stream *) conn;
+ ssize_t ret;
+ size_t left, requested;
+
+ requested = left = *len;
+ do {
+ ret = git_stream_read(io, data + (requested - left), left);
+ if (ret < 0) {
+ *len = 0;
+ return -1;
+ }
+
+ left -= ret;
+ } while (left);
+
+ *len = requested;
+
+ if (ret == 0)
+ return errSSLClosedGraceful;
+
+ return noErr;
+}
+
+ssize_t stransport_read(git_stream *stream, void *data, size_t len)
+{
+ stransport_stream *st = (stransport_stream *) stream;
+ size_t processed;
+ OSStatus ret;
+
+ if ((ret = SSLRead(st->ctx, data, len, &processed)) != noErr)
+ return stransport_error(ret);
+
+ return processed;
+}
+
+int stransport_close(git_stream *stream)
+{
+ stransport_stream *st = (stransport_stream *) stream;
+ OSStatus ret;
+
+ ret = SSLClose(st->ctx);
+ if (ret != noErr && ret != errSSLClosedGraceful)
+ return stransport_error(ret);
+
+ return git_stream_close(st->io);
+}
+
+void stransport_free(git_stream *stream)
+{
+ stransport_stream *st = (stransport_stream *) stream;
+
+ git_stream_free(st->io);
+ CFRelease(st->ctx);
+ if (st->der_data)
+ CFRelease(st->der_data);
+ git__free(st);
+}
+
+int git_stransport_stream_new(git_stream **out, const char *host, const char *port)
+{
+ stransport_stream *st;
+ int error;
+ OSStatus ret;
+
+ assert(out && host);
+
+ st = git__calloc(1, sizeof(stransport_stream));
+ GITERR_CHECK_ALLOC(st);
+
+ if ((error = git_socket_stream_new(&st->io, host, port)) < 0){
+ git__free(st);
+ return error;
+ }
+
+ st->ctx = SSLCreateContext(NULL, kSSLClientSide, kSSLStreamType);
+ if (!st->ctx) {
+ giterr_set(GITERR_NET, "failed to create SSL context");
+ return -1;
+ }
+
+ if ((ret = SSLSetIOFuncs(st->ctx, read_cb, write_cb)) != noErr ||
+ (ret = SSLSetConnection(st->ctx, st->io)) != noErr ||
+ (ret = SSLSetSessionOption(st->ctx, kSSLSessionOptionBreakOnServerAuth, true)) != noErr ||
+ (ret = SSLSetProtocolVersionMin(st->ctx, kTLSProtocol1)) != noErr ||
+ (ret = SSLSetProtocolVersionMax(st->ctx, kTLSProtocol12)) != noErr ||
+ (ret = SSLSetPeerDomainName(st->ctx, host, strlen(host))) != noErr) {
+ git_stream_free((git_stream *)st);
+ return stransport_error(ret);
+ }
+
+ st->parent.version = GIT_STREAM_VERSION;
+ st->parent.encrypted = 1;
+ st->parent.connect = stransport_connect;
+ st->parent.certificate = stransport_certificate;
+ st->parent.read = stransport_read;
+ st->parent.write = stransport_write;
+ st->parent.close = stransport_close;
+ st->parent.free = stransport_free;
+
+ *out = (git_stream *) st;
+ return 0;
+}
+
+#endif
diff --git a/src/stransport_stream.h b/src/stransport_stream.h
new file mode 100644
index 000000000..714f90273
--- /dev/null
+++ b/src/stransport_stream.h
@@ -0,0 +1,14 @@
+/*
+ * 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.
+ */
+#ifndef INCLUDE_stransport_stream_h__
+#define INCLUDE_stransport_stream_h__
+
+#include "git2/sys/stream.h"
+
+extern int git_stransport_stream_new(git_stream **out, const char *host, const char *port);
+
+#endif
diff --git a/src/tls_stream.c b/src/tls_stream.c
new file mode 100644
index 000000000..39a8ce343
--- /dev/null
+++ b/src/tls_stream.c
@@ -0,0 +1,28 @@
+/*
+ * 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 "git2/errors.h"
+#include "common.h"
+
+#include "openssl_stream.h"
+#include "stransport_stream.h"
+
+int git_tls_stream_new(git_stream **out, const char *host, const char *port)
+{
+#ifdef GIT_SECURE_TRANSPORT
+ return git_stransport_stream_new(out, host, port);
+#elif defined(GIT_OPENSSL)
+ return git_openssl_stream_new(out, host, port);
+#else
+ GIT_UNUSED(out);
+ GIT_UNUSED(host);
+ GIT_UNUSED(port);
+
+ giterr_set(GITERR_SSL, "there is no TLS stream available");
+ return -1;
+#endif
+}
diff --git a/src/tls_stream.h b/src/tls_stream.h
new file mode 100644
index 000000000..98a704174
--- /dev/null
+++ b/src/tls_stream.h
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+#ifndef INCLUDE_tls_stream_h__
+#define INCLUDE_tls_stream_h__
+
+#include "git2/sys/stream.h"
+
+/**
+ * Create a TLS stream with the most appropriate backend available for
+ * the current platform.
+ *
+ * This allows us to ask for a SecureTransport or OpenSSL stream
+ * according to being on general Unix vs OS X.
+ */
+extern int git_tls_stream_new(git_stream **out, const char *host, const char *port);
+
+#endif
diff --git a/src/transport.c b/src/transport.c
index fc9c692b8..5c65c7c06 100644
--- a/src/transport.c
+++ b/src/transport.c
@@ -29,7 +29,7 @@ static transport_definition local_transport_definition = { "file://", git_transp
static transport_definition transports[] = {
{ "git://", git_transport_smart, &git_subtransport_definition },
{ "http://", git_transport_smart, &http_subtransport_definition },
-#if defined(GIT_SSL) || defined(GIT_WINHTTP)
+#if defined(GIT_OPENSSL) || defined(GIT_WINHTTP) || defined(GIT_SECURE_TRANSPORT)
{ "https://", git_transport_smart, &http_subtransport_definition },
#endif
{ "file://", git_transport_local, NULL },
diff --git a/src/transports/http.c b/src/transports/http.c
index 6b100df7a..89cbd17d7 100644
--- a/src/transports/http.c
+++ b/src/transports/http.c
@@ -13,7 +13,7 @@
#include "smart.h"
#include "auth.h"
#include "auth_negotiate.h"
-#include "openssl_stream.h"
+#include "tls_stream.h"
#include "socket_stream.h"
git_http_auth_scheme auth_schemes[] = {
@@ -545,7 +545,7 @@ static int http_connect(http_subtransport *t)
}
if (t->connection_data.use_ssl) {
- error = git_openssl_stream_new(&t->io, t->connection_data.host, t->connection_data.port);
+ error = git_tls_stream_new(&t->io, t->connection_data.host, t->connection_data.port);
} else {
error = git_socket_stream_new(&t->io, t->connection_data.host, t->connection_data.port);
}
@@ -557,7 +557,7 @@ static int http_connect(http_subtransport *t)
error = git_stream_connect(t->io);
-#ifdef GIT_SSL
+#if defined(GIT_OPENSSL) || defined(GIT_SECURE_TRANSPORT)
if ((!error || error == GIT_ECERTIFICATE) && t->owner->certificate_check_cb != NULL &&
git_stream_is_encrypted(t->io)) {
git_cert *cert;
diff --git a/src/win32/pthread.c b/src/win32/pthread.c
index ec45ecbe5..a1cc18932 100644
--- a/src/win32/pthread.c
+++ b/src/win32/pthread.c
@@ -20,6 +20,8 @@ static DWORD WINAPI git_win32__threadproc(LPVOID lpParameter)
thread->result = thread->proc(thread->param);
+ git__free_tls_data();
+
return CLEAN_THREAD_EXIT;
}