diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/attr_file.c | 9 | ||||
| -rw-r--r-- | src/checkout.c | 2 | ||||
| -rw-r--r-- | src/config.c | 5 | ||||
| -rw-r--r-- | src/config_file.c | 165 | ||||
| -rw-r--r-- | src/diff_tform.c | 10 | ||||
| -rw-r--r-- | src/filter.c | 6 | ||||
| -rw-r--r-- | src/global.c | 32 | ||||
| -rw-r--r-- | src/global.h | 4 | ||||
| -rw-r--r-- | src/index.c | 18 | ||||
| -rw-r--r-- | src/netops.h | 4 | ||||
| -rw-r--r-- | src/openssl_stream.c | 6 | ||||
| -rw-r--r-- | src/rebase.c | 258 | ||||
| -rw-r--r-- | src/reflog.c | 2 | ||||
| -rw-r--r-- | src/remote.c | 8 | ||||
| -rw-r--r-- | src/reset.c | 6 | ||||
| -rw-r--r-- | src/settings.c | 6 | ||||
| -rw-r--r-- | src/stransport_stream.c | 249 | ||||
| -rw-r--r-- | src/stransport_stream.h | 14 | ||||
| -rw-r--r-- | src/tls_stream.c | 28 | ||||
| -rw-r--r-- | src/tls_stream.h | 21 | ||||
| -rw-r--r-- | src/transport.c | 2 | ||||
| -rw-r--r-- | src/transports/http.c | 6 | ||||
| -rw-r--r-- | src/win32/pthread.c | 2 |
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(¤t_commit, rebase->repo, &operation->id)) < 0 || + if ((error = rebase_ensure_not_dirty(rebase->repo, false, true, GIT_EUNMERGED)) < 0 || + (error = git_commit_lookup(¤t_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(¬e, rebase->repo, opts->rewrite_notes_ref, from)) < 0) { + if ((error = git_note_read(¬e, 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(¬e_id, rebase->repo, opts->rewrite_notes_ref, + error = git_note_create(¬e_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(¬es_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(¬es_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; } |
