rugged 1.1.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/lib/rugged/version.rb +1 -1
  3. data/vendor/libgit2/CMakeLists.txt +1 -1
  4. data/vendor/libgit2/deps/ntlmclient/CMakeLists.txt +1 -0
  5. data/vendor/libgit2/deps/ntlmclient/compat.h +0 -34
  6. data/vendor/libgit2/deps/ntlmclient/crypt_openssl.c +1 -1
  7. data/vendor/libgit2/deps/ntlmclient/ntlm.c +5 -5
  8. data/vendor/libgit2/deps/ntlmclient/util.c +15 -1
  9. data/vendor/libgit2/deps/ntlmclient/util.h +2 -1
  10. data/vendor/libgit2/include/git2/apply.h +2 -0
  11. data/vendor/libgit2/include/git2/blob.h +17 -1
  12. data/vendor/libgit2/include/git2/common.h +5 -0
  13. data/vendor/libgit2/include/git2/config.h +1 -1
  14. data/vendor/libgit2/include/git2/diff.h +1 -1
  15. data/vendor/libgit2/include/git2/index.h +2 -2
  16. data/vendor/libgit2/include/git2/indexer.h +2 -1
  17. data/vendor/libgit2/include/git2/odb.h +15 -20
  18. data/vendor/libgit2/include/git2/refs.h +3 -3
  19. data/vendor/libgit2/include/git2/repository.h +95 -52
  20. data/vendor/libgit2/include/git2/transport.h +1 -1
  21. data/vendor/libgit2/include/git2/tree.h +2 -0
  22. data/vendor/libgit2/include/git2/version.h +2 -2
  23. data/vendor/libgit2/src/blame.c +15 -10
  24. data/vendor/libgit2/src/blob.c +9 -0
  25. data/vendor/libgit2/src/clone.c +42 -21
  26. data/vendor/libgit2/src/config_cache.c +6 -3
  27. data/vendor/libgit2/src/diff_tform.c +2 -2
  28. data/vendor/libgit2/src/index.c +6 -5
  29. data/vendor/libgit2/src/indexer.c +19 -3
  30. data/vendor/libgit2/src/integer.h +15 -0
  31. data/vendor/libgit2/src/merge.c +5 -2
  32. data/vendor/libgit2/src/mwindow.c +3 -1
  33. data/vendor/libgit2/src/odb.c +4 -3
  34. data/vendor/libgit2/src/pack.c +10 -6
  35. data/vendor/libgit2/src/posix.c +32 -9
  36. data/vendor/libgit2/src/posix.h +9 -0
  37. data/vendor/libgit2/src/refdb_fs.c +4 -2
  38. data/vendor/libgit2/src/refs.c +20 -7
  39. data/vendor/libgit2/src/refs.h +1 -1
  40. data/vendor/libgit2/src/refspec.c +48 -32
  41. data/vendor/libgit2/src/remote.c +13 -7
  42. data/vendor/libgit2/src/repository.c +15 -15
  43. data/vendor/libgit2/src/repository.h +1 -1
  44. data/vendor/libgit2/src/thread-utils.h +24 -19
  45. data/vendor/libgit2/src/transports/httpclient.c +9 -4
  46. data/vendor/libgit2/src/transports/winhttp.c +99 -52
  47. data/vendor/libgit2/src/unix/posix.h +3 -0
  48. data/vendor/libgit2/src/win32/posix_w32.c +70 -0
  49. data/vendor/libgit2/src/worktree.c +8 -1
  50. metadata +2 -2
@@ -89,6 +89,12 @@
89
89
  #define EAFNOSUPPORT (INT_MAX-1)
90
90
  #endif
91
91
 
92
+ /* Compiler independent macro to handle signal interrpted system calls */
93
+ #define HANDLE_EINTR(result, x) do { \
94
+ result = (x); \
95
+ } while (result == -1 && errno == EINTR);
96
+
97
+
92
98
  /* Provide a 64-bit size for offsets. */
93
99
 
94
100
  #if defined(_MSC_VER)
@@ -119,6 +125,9 @@ typedef int git_file;
119
125
  extern ssize_t p_read(git_file fd, void *buf, size_t cnt);
120
126
  extern int p_write(git_file fd, const void *buf, size_t cnt);
121
127
 
128
+ extern ssize_t p_pread(int fd, void *data, size_t size, off64_t offset);
129
+ extern ssize_t p_pwrite(int fd, const void *data, size_t size, off64_t offset);
130
+
122
131
  #define p_close(fd) close(fd)
123
132
  #define p_umask(m) umask(m)
124
133
 
@@ -254,7 +254,8 @@ static int loose_lookup_to_packfile(refdb_fs_backend *backend, const char *name)
254
254
  if ((error = loose_parse_oid(&oid, name, &ref_file)) < 0)
255
255
  goto done;
256
256
 
257
- git_sortedcache_wlock(backend->refcache);
257
+ if ((error = git_sortedcache_wlock(backend->refcache)) < 0)
258
+ goto done;
258
259
 
259
260
  if (!(error = git_sortedcache_upsert(
260
261
  (void **)&ref, backend->refcache, name))) {
@@ -760,7 +761,8 @@ static int reference_path_available(
760
761
  }
761
762
  }
762
763
 
763
- git_sortedcache_rlock(backend->refcache);
764
+ if ((error = git_sortedcache_rlock(backend->refcache)) < 0)
765
+ return error;
764
766
 
765
767
  for (i = 0; i < git_sortedcache_entrycount(backend->refcache); ++i) {
766
768
  struct packref *ref = git_sortedcache_entry(backend->refcache, i);
@@ -1287,19 +1287,32 @@ cleanup:
1287
1287
  return error;
1288
1288
  }
1289
1289
 
1290
- int git_reference__is_valid_name(const char *refname, unsigned int flags)
1290
+ int git_reference__name_is_valid(
1291
+ int *valid,
1292
+ const char *refname,
1293
+ unsigned int flags)
1291
1294
  {
1292
- if (git_reference__normalize_name(NULL, refname, flags) < 0) {
1293
- git_error_clear();
1294
- return false;
1295
- }
1295
+ int error;
1296
1296
 
1297
- return true;
1297
+ *valid = 0;
1298
+
1299
+ error = git_reference__normalize_name(NULL, refname, flags);
1300
+
1301
+ if (!error)
1302
+ *valid = 1;
1303
+ else if (error == GIT_EINVALIDSPEC)
1304
+ error = 0;
1305
+
1306
+ return error;
1298
1307
  }
1299
1308
 
1300
1309
  int git_reference_is_valid_name(const char *refname)
1301
1310
  {
1302
- return git_reference__is_valid_name(refname, GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL);
1311
+ int valid = 0;
1312
+
1313
+ git_reference__name_is_valid(&valid, refname, GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL);
1314
+
1315
+ return valid;
1303
1316
  }
1304
1317
 
1305
1318
  const char *git_reference__shorthand(const char *name)
@@ -85,7 +85,7 @@ git_reference *git_reference__realloc(git_reference **ptr_to_ref, const char *na
85
85
 
86
86
  int git_reference__normalize_name(git_buf *buf, const char *name, unsigned int flags);
87
87
  int git_reference__update_terminal(git_repository *repo, const char *ref_name, const git_oid *oid, const git_signature *sig, const char *log_message);
88
- int git_reference__is_valid_name(const char *refname, unsigned int flags);
88
+ int git_reference__name_is_valid(int *valid, const char *name, unsigned int flags);
89
89
  int git_reference__is_branch(const char *ref_name);
90
90
  int git_reference__is_remote(const char *ref_name);
91
91
  int git_reference__is_tag(const char *ref_name);
@@ -21,7 +21,8 @@ int git_refspec__parse(git_refspec *refspec, const char *input, bool is_fetch)
21
21
  size_t llen;
22
22
  int is_glob = 0;
23
23
  const char *lhs, *rhs;
24
- int flags;
24
+ int valid = 0;
25
+ unsigned int flags;
25
26
 
26
27
  assert(refspec && input);
27
28
 
@@ -75,57 +76,69 @@ int git_refspec__parse(git_refspec *refspec, const char *input, bool is_fetch)
75
76
 
76
77
  if (is_fetch) {
77
78
  /*
78
- * LHS
79
- * - empty is allowed; it means HEAD.
80
- * - otherwise it must be a valid looking ref.
81
- */
79
+ * LHS
80
+ * - empty is allowed; it means HEAD.
81
+ * - otherwise it must be a valid looking ref.
82
+ */
82
83
  if (!*refspec->src)
83
84
  ; /* empty is ok */
84
- else if (!git_reference__is_valid_name(refspec->src, flags))
85
+ else if (git_reference__name_is_valid(&valid, refspec->src, flags) < 0)
86
+ goto on_error;
87
+ else if (!valid)
85
88
  goto invalid;
89
+
86
90
  /*
87
- * RHS
88
- * - missing is ok, and is same as empty.
89
- * - empty is ok; it means not to store.
90
- * - otherwise it must be a valid looking ref.
91
- */
91
+ * RHS
92
+ * - missing is ok, and is same as empty.
93
+ * - empty is ok; it means not to store.
94
+ * - otherwise it must be a valid looking ref.
95
+ */
92
96
  if (!refspec->dst)
93
97
  ; /* ok */
94
98
  else if (!*refspec->dst)
95
99
  ; /* ok */
96
- else if (!git_reference__is_valid_name(refspec->dst, flags))
100
+ else if (git_reference__name_is_valid(&valid, refspec->dst, flags) < 0)
101
+ goto on_error;
102
+ else if (!valid)
97
103
  goto invalid;
98
104
  } else {
99
105
  /*
100
- * LHS
101
- * - empty is allowed; it means delete.
102
- * - when wildcarded, it must be a valid looking ref.
103
- * - otherwise, it must be an extended SHA-1, but
104
- * there is no existing way to validate this.
105
- */
106
+ * LHS
107
+ * - empty is allowed; it means delete.
108
+ * - when wildcarded, it must be a valid looking ref.
109
+ * - otherwise, it must be an extended SHA-1, but
110
+ * there is no existing way to validate this.
111
+ */
106
112
  if (!*refspec->src)
107
113
  ; /* empty is ok */
108
114
  else if (is_glob) {
109
- if (!git_reference__is_valid_name(refspec->src, flags))
115
+ if (git_reference__name_is_valid(&valid, refspec->src, flags) < 0)
116
+ goto on_error;
117
+ else if (!valid)
110
118
  goto invalid;
111
119
  }
112
120
  else {
113
121
  ; /* anything goes, for now */
114
122
  }
123
+
115
124
  /*
116
- * RHS
117
- * - missing is allowed, but LHS then must be a
118
- * valid looking ref.
119
- * - empty is not allowed.
120
- * - otherwise it must be a valid looking ref.
121
- */
125
+ * RHS
126
+ * - missing is allowed, but LHS then must be a
127
+ * valid looking ref.
128
+ * - empty is not allowed.
129
+ * - otherwise it must be a valid looking ref.
130
+ */
122
131
  if (!refspec->dst) {
123
- if (!git_reference__is_valid_name(refspec->src, flags))
132
+ if (git_reference__name_is_valid(&valid, refspec->src, flags) < 0)
133
+ goto on_error;
134
+ else if (!valid)
124
135
  goto invalid;
125
136
  } else if (!*refspec->dst) {
126
137
  goto invalid;
127
138
  } else {
128
- if (!git_reference__is_valid_name(refspec->dst, flags))
139
+ if (git_reference__name_is_valid(&valid, refspec->dst, flags) < 0)
140
+ goto on_error;
141
+ else if (!valid)
129
142
  goto invalid;
130
143
  }
131
144
 
@@ -141,11 +154,14 @@ int git_refspec__parse(git_refspec *refspec, const char *input, bool is_fetch)
141
154
 
142
155
  return 0;
143
156
 
144
- invalid:
145
- git_error_set(
146
- GIT_ERROR_INVALID,
147
- "'%s' is not a valid refspec.", input);
148
- git_refspec__dispose(refspec);
157
+ invalid:
158
+ git_error_set(GIT_ERROR_INVALID,
159
+ "'%s' is not a valid refspec.", input);
160
+ git_refspec__dispose(refspec);
161
+ return GIT_EINVALIDSPEC;
162
+
163
+ on_error:
164
+ git_refspec__dispose(refspec);
149
165
  return -1;
150
166
  }
151
167
 
@@ -110,12 +110,8 @@ static int write_add_refspec(git_repository *repo, const char *name, const char
110
110
  if ((error = ensure_remote_name_is_valid(name)) < 0)
111
111
  return error;
112
112
 
113
- if ((error = git_refspec__parse(&spec, refspec, fetch)) < 0) {
114
- if (git_error_last()->klass != GIT_ERROR_NOMEMORY)
115
- error = GIT_EINVALIDSPEC;
116
-
113
+ if ((error = git_refspec__parse(&spec, refspec, fetch)) < 0)
117
114
  return error;
118
- }
119
115
 
120
116
  git_refspec__dispose(&spec);
121
117
 
@@ -1116,7 +1112,7 @@ static int remote_head_for_ref(git_remote_head **out, git_remote *remote, git_re
1116
1112
  git_reference *resolved_ref = NULL;
1117
1113
  git_buf remote_name = GIT_BUF_INIT;
1118
1114
  git_config *config = NULL;
1119
- const char *ref_name;
1115
+ const char *ref_name = NULL;
1120
1116
  int error = 0, update;
1121
1117
 
1122
1118
  assert(out && spec && ref);
@@ -1129,10 +1125,20 @@ static int remote_head_for_ref(git_remote_head **out, git_remote *remote, git_re
1129
1125
  if (error == GIT_ENOTFOUND && git_reference_type(ref) == GIT_REFERENCE_SYMBOLIC) {
1130
1126
  ref_name = git_reference_symbolic_target(ref);
1131
1127
  error = 0;
1132
- } else {
1128
+ } else if (resolved_ref) {
1133
1129
  ref_name = git_reference_name(resolved_ref);
1134
1130
  }
1135
1131
 
1132
+ /*
1133
+ * The ref name may be unresolvable - perhaps it's pointing to
1134
+ * something invalid. In this case, there is no remote head for
1135
+ * this ref.
1136
+ */
1137
+ if (!ref_name) {
1138
+ error = 0;
1139
+ goto cleanup;
1140
+ }
1141
+
1136
1142
  if ((error = ref_to_update(&update, &remote_name, remote, spec, ref_name)) < 0)
1137
1143
  goto cleanup;
1138
1144
 
@@ -2078,7 +2078,8 @@ static int repo_init_head(const char *repo_dir, const char *given)
2078
2078
  if (given) {
2079
2079
  initial_head = given;
2080
2080
  } else if ((error = git_config_open_default(&cfg)) >= 0 &&
2081
- (error = git_config_get_string_buf(&cfg_branch, cfg, "init.defaultbranch")) >= 0) {
2081
+ (error = git_config_get_string_buf(&cfg_branch, cfg, "init.defaultbranch")) >= 0 &&
2082
+ *cfg_branch.ptr) {
2082
2083
  initial_head = cfg_branch.ptr;
2083
2084
  }
2084
2085
 
@@ -2334,21 +2335,19 @@ int git_repository_head_unborn(git_repository *repo)
2334
2335
  return 0;
2335
2336
  }
2336
2337
 
2337
- static int at_least_one_cb(const char *refname, void *payload)
2338
- {
2339
- GIT_UNUSED(refname);
2340
- GIT_UNUSED(payload);
2341
- return GIT_PASSTHROUGH;
2342
- }
2343
-
2344
2338
  static int repo_contains_no_reference(git_repository *repo)
2345
2339
  {
2346
- int error = git_reference_foreach_name(repo, &at_least_one_cb, NULL);
2340
+ git_reference_iterator *iter;
2341
+ const char *refname;
2342
+ int error;
2347
2343
 
2348
- if (error == GIT_PASSTHROUGH)
2349
- return 0;
2344
+ if ((error = git_reference_iterator_new(&iter, repo)) < 0)
2345
+ return error;
2350
2346
 
2351
- if (!error)
2347
+ error = git_reference_next_name(&refname, iter);
2348
+ git_reference_iterator_free(iter);
2349
+
2350
+ if (error == GIT_ITEROVER)
2352
2351
  return 1;
2353
2352
 
2354
2353
  return error;
@@ -2364,10 +2363,11 @@ int git_repository_initialbranch(git_buf *out, git_repository *repo)
2364
2363
  if ((error = git_repository_config__weakptr(&config, repo)) < 0)
2365
2364
  return error;
2366
2365
 
2367
- if ((error = git_config_get_entry(&entry, config, "init.defaultbranch")) == 0) {
2366
+ if ((error = git_config_get_entry(&entry, config, "init.defaultbranch")) == 0 &&
2367
+ *entry->value) {
2368
2368
  branch = entry->value;
2369
2369
  }
2370
- else if (error == GIT_ENOTFOUND) {
2370
+ else if (!error || error == GIT_ENOTFOUND) {
2371
2371
  branch = GIT_BRANCH_DEFAULT;
2372
2372
  }
2373
2373
  else {
@@ -2379,7 +2379,7 @@ int git_repository_initialbranch(git_buf *out, git_repository *repo)
2379
2379
  goto done;
2380
2380
 
2381
2381
  if (!git_reference_is_valid_name(out->ptr)) {
2382
- git_error_set(GIT_ERROR_INVALID, "the value of init.defaultBranch is not a valid reference name");
2382
+ git_error_set(GIT_ERROR_INVALID, "the value of init.defaultBranch is not a valid branch name");
2383
2383
  error = -1;
2384
2384
  }
2385
2385
 
@@ -154,7 +154,7 @@ struct git_repository {
154
154
 
155
155
  git_atomic attr_session_key;
156
156
 
157
- git_configmap_value configmap_cache[GIT_CONFIGMAP_CACHE_MAX];
157
+ intptr_t configmap_cache[GIT_CONFIGMAP_CACHE_MAX];
158
158
  git_strmap *submodule_cache;
159
159
  };
160
160
 
@@ -235,35 +235,35 @@ GIT_INLINE(int64_t) git_atomic64_get(git_atomic64 *a)
235
235
 
236
236
  #else
237
237
 
238
+ GIT_INLINE(int) git___noop(void) { return 0; }
239
+
238
240
  #define git_thread unsigned int
239
- #define git_thread_create(thread, start_routine, arg) 0
240
- #define git_thread_join(id, status) (void)0
241
+ #define git_thread_create(thread, start_routine, arg) git___noop()
242
+ #define git_thread_join(id, status) git___noop()
241
243
 
242
244
  /* Pthreads Mutex */
243
245
  #define git_mutex unsigned int
244
- GIT_INLINE(int) git_mutex_init(git_mutex *mutex) \
245
- { GIT_UNUSED(mutex); return 0; }
246
- GIT_INLINE(int) git_mutex_lock(git_mutex *mutex) \
247
- { GIT_UNUSED(mutex); return 0; }
248
- #define git_mutex_unlock(a) (void)0
249
- #define git_mutex_free(a) (void)0
246
+ #define git_mutex_init(a) git___noop()
247
+ #define git_mutex_lock(a) git___noop()
248
+ #define git_mutex_unlock(a) git___noop()
249
+ #define git_mutex_free(a) git___noop()
250
250
 
251
251
  /* Pthreads condition vars */
252
252
  #define git_cond unsigned int
253
- #define git_cond_init(c, a) (void)0
254
- #define git_cond_free(c) (void)0
255
- #define git_cond_wait(c, l) (void)0
256
- #define git_cond_signal(c) (void)0
257
- #define git_cond_broadcast(c) (void)0
253
+ #define git_cond_init(c, a) git___noop()
254
+ #define git_cond_free(c) git___noop()
255
+ #define git_cond_wait(c, l) git___noop()
256
+ #define git_cond_signal(c) git___noop()
257
+ #define git_cond_broadcast(c) git___noop()
258
258
 
259
259
  /* Pthreads rwlock */
260
260
  #define git_rwlock unsigned int
261
- #define git_rwlock_init(a) 0
262
- #define git_rwlock_rdlock(a) 0
263
- #define git_rwlock_rdunlock(a) (void)0
264
- #define git_rwlock_wrlock(a) 0
265
- #define git_rwlock_wrunlock(a) (void)0
266
- #define git_rwlock_free(a) (void)0
261
+ #define git_rwlock_init(a) git___noop()
262
+ #define git_rwlock_rdlock(a) git___noop()
263
+ #define git_rwlock_rdunlock(a) git___noop()
264
+ #define git_rwlock_wrlock(a) git___noop()
265
+ #define git_rwlock_wrunlock(a) git___noop()
266
+ #define git_rwlock_free(a) git___noop()
267
267
  #define GIT_RWLOCK_STATIC_INIT 0
268
268
 
269
269
 
@@ -311,6 +311,11 @@ GIT_INLINE(volatile void *) git___swap(
311
311
  return old;
312
312
  }
313
313
 
314
+ GIT_INLINE(volatile void *) git___load(void * volatile *ptr)
315
+ {
316
+ return *ptr;
317
+ }
318
+
314
319
  #ifdef GIT_ARCH_64
315
320
 
316
321
  GIT_INLINE(int64_t) git_atomic64_add(git_atomic64 *a, int64_t addend)
@@ -658,6 +658,11 @@ static int generate_connect_request(
658
658
  return git_buf_oom(buf) ? -1 : 0;
659
659
  }
660
660
 
661
+ static bool use_connect_proxy(git_http_client *client)
662
+ {
663
+ return client->proxy.url.host && !strcmp(client->server.url.scheme, "https");
664
+ }
665
+
661
666
  static int generate_request(
662
667
  git_http_client *client,
663
668
  git_http_request *request)
@@ -713,7 +718,8 @@ static int generate_request(
713
718
  git_buf_printf(buf, "Expect: 100-continue\r\n");
714
719
 
715
720
  if ((error = apply_server_credentials(buf, client, request)) < 0 ||
716
- (error = apply_proxy_credentials(buf, client, request)) < 0)
721
+ (!use_connect_proxy(client) &&
722
+ (error = apply_proxy_credentials(buf, client, request)) < 0))
717
723
  return error;
718
724
 
719
725
  if (request->custom_headers) {
@@ -1003,8 +1009,7 @@ static int http_client_connect(
1003
1009
  reset_parser(client);
1004
1010
 
1005
1011
  /* Reconnect to the proxy if necessary. */
1006
- use_proxy = client->proxy.url.host &&
1007
- !strcmp(client->server.url.scheme, "https");
1012
+ use_proxy = use_connect_proxy(client);
1008
1013
 
1009
1014
  if (use_proxy) {
1010
1015
  if (!client->proxy_connected || !client->keepalive ||
@@ -1476,7 +1481,7 @@ int git_http_client_skip_body(git_http_client *client)
1476
1481
  "unexpected data handled in callback");
1477
1482
  error = -1;
1478
1483
  }
1479
- } while (!error);
1484
+ } while (error >= 0 && client->state != DONE);
1480
1485
 
1481
1486
  if (error < 0)
1482
1487
  client->connected = 0;
@@ -53,6 +53,10 @@
53
53
  # define WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_3 0x00002000
54
54
  #endif
55
55
 
56
+ #ifndef WINHTTP_NO_CLIENT_CERT_CONTEXT
57
+ # define WINHTTP_NO_CLIENT_CERT_CONTEXT NULL
58
+ #endif
59
+
56
60
  #ifndef HTTP_STATUS_PERMANENT_REDIRECT
57
61
  # define HTTP_STATUS_PERMANENT_REDIRECT 308
58
62
  #endif
@@ -112,7 +116,8 @@ typedef struct {
112
116
  DWORD post_body_len;
113
117
  unsigned sent_request : 1,
114
118
  received_response : 1,
115
- chunked : 1;
119
+ chunked : 1,
120
+ status_sending_request_reached: 1;
116
121
  } winhttp_stream;
117
122
 
118
123
  typedef struct {
@@ -708,30 +713,36 @@ static void CALLBACK winhttp_status(
708
713
  DWORD status;
709
714
 
710
715
  GIT_UNUSED(connection);
711
- GIT_UNUSED(ctx);
712
716
  GIT_UNUSED(info_len);
713
717
 
714
- if (code != WINHTTP_CALLBACK_STATUS_SECURE_FAILURE)
715
- return;
716
-
717
- status = *((DWORD *)info);
718
-
719
- if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_CN_INVALID))
720
- git_error_set(GIT_ERROR_HTTP, "SSL certificate issued for different common name");
721
- else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_DATE_INVALID))
722
- git_error_set(GIT_ERROR_HTTP, "SSL certificate has expired");
723
- else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CA))
724
- git_error_set(GIT_ERROR_HTTP, "SSL certificate signed by unknown CA");
725
- else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CERT))
726
- git_error_set(GIT_ERROR_HTTP, "SSL certificate is invalid");
727
- else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_REV_FAILED))
728
- git_error_set(GIT_ERROR_HTTP, "certificate revocation check failed");
729
- else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_REVOKED))
730
- git_error_set(GIT_ERROR_HTTP, "SSL certificate was revoked");
731
- else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_SECURITY_CHANNEL_ERROR))
732
- git_error_set(GIT_ERROR_HTTP, "security libraries could not be loaded");
733
- else
734
- git_error_set(GIT_ERROR_HTTP, "unknown security error %lu", status);
718
+ switch (code) {
719
+ case WINHTTP_CALLBACK_STATUS_SECURE_FAILURE:
720
+ status = *((DWORD *)info);
721
+
722
+ if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_CN_INVALID))
723
+ git_error_set(GIT_ERROR_HTTP, "SSL certificate issued for different common name");
724
+ else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_DATE_INVALID))
725
+ git_error_set(GIT_ERROR_HTTP, "SSL certificate has expired");
726
+ else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CA))
727
+ git_error_set(GIT_ERROR_HTTP, "SSL certificate signed by unknown CA");
728
+ else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CERT))
729
+ git_error_set(GIT_ERROR_HTTP, "SSL certificate is invalid");
730
+ else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_REV_FAILED))
731
+ git_error_set(GIT_ERROR_HTTP, "certificate revocation check failed");
732
+ else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_REVOKED))
733
+ git_error_set(GIT_ERROR_HTTP, "SSL certificate was revoked");
734
+ else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_SECURITY_CHANNEL_ERROR))
735
+ git_error_set(GIT_ERROR_HTTP, "security libraries could not be loaded");
736
+ else
737
+ git_error_set(GIT_ERROR_HTTP, "unknown security error %lu", status);
738
+
739
+ break;
740
+
741
+ case WINHTTP_CALLBACK_STATUS_SENDING_REQUEST:
742
+ ((winhttp_stream *) ctx)->status_sending_request_reached = 1;
743
+
744
+ break;
745
+ }
735
746
  }
736
747
 
737
748
  static int winhttp_connect(
@@ -826,7 +837,12 @@ static int winhttp_connect(
826
837
  goto on_error;
827
838
  }
828
839
 
829
- if (WinHttpSetStatusCallback(t->connection, winhttp_status, WINHTTP_CALLBACK_FLAG_SECURE_FAILURE, 0) == WINHTTP_INVALID_STATUS_CALLBACK) {
840
+ if (WinHttpSetStatusCallback(
841
+ t->connection,
842
+ winhttp_status,
843
+ WINHTTP_CALLBACK_FLAG_SECURE_FAILURE | WINHTTP_CALLBACK_FLAG_SEND_REQUEST,
844
+ 0
845
+ ) == WINHTTP_INVALID_STATUS_CALLBACK) {
830
846
  git_error_set(GIT_ERROR_OS, "failed to set status callback");
831
847
  goto on_error;
832
848
  }
@@ -858,12 +874,12 @@ static int do_send_request(winhttp_stream *s, size_t len, bool chunked)
858
874
  success = WinHttpSendRequest(s->request,
859
875
  WINHTTP_NO_ADDITIONAL_HEADERS, 0,
860
876
  WINHTTP_NO_REQUEST_DATA, 0,
861
- WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH, 0);
877
+ WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH, (DWORD_PTR)s);
862
878
  } else {
863
879
  success = WinHttpSendRequest(s->request,
864
880
  WINHTTP_NO_ADDITIONAL_HEADERS, 0,
865
881
  WINHTTP_NO_REQUEST_DATA, 0,
866
- (DWORD)len, 0);
882
+ (DWORD)len, (DWORD_PTR)s);
867
883
  }
868
884
 
869
885
  if (success || GetLastError() != (DWORD)SEC_E_BUFFER_TOO_SMALL)
@@ -875,42 +891,73 @@ static int do_send_request(winhttp_stream *s, size_t len, bool chunked)
875
891
 
876
892
  static int send_request(winhttp_stream *s, size_t len, bool chunked)
877
893
  {
878
- int request_failed = 0, cert_valid = 1, error = 0;
879
- DWORD ignore_flags;
894
+ int request_failed = 1, error, attempts = 0;
895
+ DWORD ignore_flags, send_request_error;
880
896
 
881
897
  git_error_clear();
882
- if ((error = do_send_request(s, len, chunked)) < 0) {
883
- if (GetLastError() != ERROR_WINHTTP_SECURE_FAILURE) {
884
- git_error_set(GIT_ERROR_OS, "failed to send request");
885
- return -1;
886
- }
887
898
 
888
- request_failed = 1;
889
- cert_valid = 0;
890
- }
899
+ while (request_failed && attempts++ < 3) {
900
+ int cert_valid = 1;
901
+ int client_cert_requested = 0;
902
+ request_failed = 0;
903
+
904
+ if ((error = do_send_request(s, len, chunked)) < 0) {
905
+ send_request_error = GetLastError();
906
+ request_failed = 1;
907
+
908
+ switch (send_request_error) {
909
+ case ERROR_WINHTTP_SECURE_FAILURE:
910
+ cert_valid = 0;
911
+ break;
912
+ case ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED:
913
+ client_cert_requested = 1;
914
+ break;
915
+ default:
916
+ git_error_set(GIT_ERROR_OS, "failed to send request");
917
+ return -1;
918
+ }
919
+ }
891
920
 
892
- git_error_clear();
893
- if ((error = certificate_check(s, cert_valid)) < 0) {
894
- if (!git_error_last())
895
- git_error_set(GIT_ERROR_OS, "user cancelled certificate check");
921
+ /*
922
+ * Only check the certificate if we were able to reach the sending request phase, or
923
+ * received a secure failure error. Otherwise, the server certificate won't be available
924
+ * since the request wasn't able to complete (e.g. proxy auth required)
925
+ */
926
+ if (!cert_valid ||
927
+ (!request_failed && s->status_sending_request_reached)) {
928
+ git_error_clear();
929
+ if ((error = certificate_check(s, cert_valid)) < 0) {
930
+ if (!git_error_last())
931
+ git_error_set(GIT_ERROR_OS, "user cancelled certificate check");
896
932
 
897
- return error;
898
- }
933
+ return error;
934
+ }
935
+ }
899
936
 
900
- /* if neither the request nor the certificate check returned errors, we're done */
901
- if (!request_failed)
902
- return 0;
937
+ /* if neither the request nor the certificate check returned errors, we're done */
938
+ if (!request_failed)
939
+ return 0;
903
940
 
904
- ignore_flags = no_check_cert_flags;
941
+ if (!cert_valid) {
942
+ ignore_flags = no_check_cert_flags;
943
+ if (!WinHttpSetOption(s->request, WINHTTP_OPTION_SECURITY_FLAGS, &ignore_flags, sizeof(ignore_flags))) {
944
+ git_error_set(GIT_ERROR_OS, "failed to set security options");
945
+ return -1;
946
+ }
947
+ }
905
948
 
906
- if (!WinHttpSetOption(s->request, WINHTTP_OPTION_SECURITY_FLAGS, &ignore_flags, sizeof(ignore_flags))) {
907
- git_error_set(GIT_ERROR_OS, "failed to set security options");
908
- return -1;
949
+ if (client_cert_requested) {
950
+ /*
951
+ * Client certificates are not supported, explicitly tell the server that
952
+ * (it's possible a client certificate was requested but is not required)
953
+ */
954
+ if (!WinHttpSetOption(s->request, WINHTTP_OPTION_CLIENT_CERT_CONTEXT, WINHTTP_NO_CLIENT_CERT_CONTEXT, 0)) {
955
+ git_error_set(GIT_ERROR_OS, "failed to set client cert context");
956
+ return -1;
957
+ }
958
+ }
909
959
  }
910
960
 
911
- if ((error = do_send_request(s, len, chunked)) < 0)
912
- git_error_set(GIT_ERROR_OS, "failed to send request with unchecked certificate");
913
-
914
961
  return error;
915
962
  }
916
963