rugged 0.25.0b2 → 0.25.0b3

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 (99) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +1 -1
  3. data/ext/rugged/extconf.rb +3 -1
  4. data/ext/rugged/rugged.c +1 -1
  5. data/ext/rugged/rugged.h +1 -1
  6. data/ext/rugged/rugged_blob.c +29 -38
  7. data/ext/rugged/rugged_commit.c +215 -78
  8. data/ext/rugged/rugged_rebase.c +18 -11
  9. data/ext/rugged/rugged_remote.c +2 -2
  10. data/ext/rugged/rugged_tree.c +132 -0
  11. data/lib/rugged/version.rb +1 -1
  12. data/vendor/libgit2/CMakeLists.txt +11 -3
  13. data/vendor/libgit2/include/git2.h +1 -0
  14. data/vendor/libgit2/include/git2/blob.h +39 -28
  15. data/vendor/libgit2/include/git2/commit.h +30 -0
  16. data/vendor/libgit2/include/git2/common.h +16 -1
  17. data/vendor/libgit2/include/git2/merge.h +10 -1
  18. data/vendor/libgit2/include/git2/proxy.h +92 -0
  19. data/vendor/libgit2/include/git2/refs.h +11 -0
  20. data/vendor/libgit2/include/git2/remote.h +17 -4
  21. data/vendor/libgit2/include/git2/signature.h +13 -0
  22. data/vendor/libgit2/include/git2/sys/merge.h +177 -0
  23. data/vendor/libgit2/include/git2/sys/remote.h +16 -0
  24. data/vendor/libgit2/include/git2/sys/stream.h +2 -1
  25. data/vendor/libgit2/include/git2/sys/transport.h +3 -1
  26. data/vendor/libgit2/include/git2/tag.h +9 -0
  27. data/vendor/libgit2/include/git2/tree.h +55 -0
  28. data/vendor/libgit2/src/annotated_commit.c +99 -80
  29. data/vendor/libgit2/src/annotated_commit.h +5 -2
  30. data/vendor/libgit2/src/array.h +40 -0
  31. data/vendor/libgit2/src/blame.c +8 -3
  32. data/vendor/libgit2/src/blame_git.c +2 -1
  33. data/vendor/libgit2/src/blob.c +71 -39
  34. data/vendor/libgit2/src/branch.c +2 -1
  35. data/vendor/libgit2/src/checkout.c +66 -42
  36. data/vendor/libgit2/src/commit.c +67 -3
  37. data/vendor/libgit2/src/config_cache.c +2 -1
  38. data/vendor/libgit2/src/config_file.c +32 -27
  39. data/vendor/libgit2/src/curl_stream.c +89 -6
  40. data/vendor/libgit2/src/delta-apply.c +36 -5
  41. data/vendor/libgit2/src/delta-apply.h +12 -0
  42. data/vendor/libgit2/src/describe.c +3 -2
  43. data/vendor/libgit2/src/diff.c +13 -20
  44. data/vendor/libgit2/src/diff_tform.c +5 -3
  45. data/vendor/libgit2/src/filebuf.c +12 -2
  46. data/vendor/libgit2/src/filebuf.h +1 -0
  47. data/vendor/libgit2/src/fnmatch.c +18 -5
  48. data/vendor/libgit2/src/global.c +18 -0
  49. data/vendor/libgit2/src/global.h +1 -0
  50. data/vendor/libgit2/src/ignore.c +11 -3
  51. data/vendor/libgit2/src/index.c +11 -5
  52. data/vendor/libgit2/src/indexer.c +11 -7
  53. data/vendor/libgit2/src/iterator.c +1575 -1468
  54. data/vendor/libgit2/src/iterator.h +52 -69
  55. data/vendor/libgit2/src/merge.c +160 -63
  56. data/vendor/libgit2/src/merge.h +61 -2
  57. data/vendor/libgit2/src/merge_driver.c +397 -0
  58. data/vendor/libgit2/src/merge_driver.h +60 -0
  59. data/vendor/libgit2/src/merge_file.c +11 -49
  60. data/vendor/libgit2/src/netops.c +12 -10
  61. data/vendor/libgit2/src/object.c +3 -6
  62. data/vendor/libgit2/src/object_api.c +19 -1
  63. data/vendor/libgit2/src/odb_loose.c +1 -1
  64. data/vendor/libgit2/src/openssl_stream.c +16 -3
  65. data/vendor/libgit2/src/pack-objects.c +3 -1
  66. data/vendor/libgit2/src/pack.c +5 -9
  67. data/vendor/libgit2/src/path.c +14 -0
  68. data/vendor/libgit2/src/path.h +12 -0
  69. data/vendor/libgit2/src/pathspec.c +1 -1
  70. data/vendor/libgit2/src/posix.c +7 -0
  71. data/vendor/libgit2/src/posix.h +1 -0
  72. data/vendor/libgit2/src/proxy.c +32 -0
  73. data/vendor/libgit2/src/proxy.h +14 -0
  74. data/vendor/libgit2/src/push.c +7 -7
  75. data/vendor/libgit2/src/rebase.c +61 -31
  76. data/vendor/libgit2/src/refdb_fs.c +1 -0
  77. data/vendor/libgit2/src/refs.c +16 -1
  78. data/vendor/libgit2/src/remote.c +20 -6
  79. data/vendor/libgit2/src/repository.c +1 -1
  80. data/vendor/libgit2/src/reset.c +1 -1
  81. data/vendor/libgit2/src/settings.c +23 -1
  82. data/vendor/libgit2/src/signature.c +26 -1
  83. data/vendor/libgit2/src/stransport_stream.c +5 -2
  84. data/vendor/libgit2/src/stream.h +2 -2
  85. data/vendor/libgit2/src/submodule.c +3 -2
  86. data/vendor/libgit2/src/tag.c +8 -2
  87. data/vendor/libgit2/src/transports/http.c +32 -9
  88. data/vendor/libgit2/src/transports/local.c +4 -1
  89. data/vendor/libgit2/src/transports/smart.c +6 -0
  90. data/vendor/libgit2/src/transports/smart.h +1 -0
  91. data/vendor/libgit2/src/transports/smart_protocol.c +61 -17
  92. data/vendor/libgit2/src/transports/winhttp.c +130 -11
  93. data/vendor/libgit2/src/tree.c +329 -98
  94. data/vendor/libgit2/src/tree.h +4 -5
  95. data/vendor/libgit2/src/unix/map.c +5 -0
  96. data/vendor/libgit2/src/win32/map.c +24 -5
  97. data/vendor/libgit2/src/xdiff/xprepare.c +2 -1
  98. metadata +10 -4
  99. data/vendor/libgit2/Makefile.embed +0 -60
@@ -200,7 +200,8 @@ int git_signature__parse(git_signature *sig, const char **buffer_out,
200
200
 
201
201
  memset(sig, 0, sizeof(git_signature));
202
202
 
203
- if ((buffer_end = memchr(buffer, ender, buffer_end - buffer)) == NULL)
203
+ if (ender &&
204
+ (buffer_end = memchr(buffer, ender, buffer_end - buffer)) == NULL)
204
205
  return signature_error("no newline given");
205
206
 
206
207
  if (header) {
@@ -262,6 +263,30 @@ int git_signature__parse(git_signature *sig, const char **buffer_out,
262
263
  return 0;
263
264
  }
264
265
 
266
+ int git_signature_from_buffer(git_signature **out, const char *buf)
267
+ {
268
+ git_signature *sig;
269
+ const char *buf_end;
270
+ int error;
271
+
272
+ assert(out && buf);
273
+
274
+ *out = NULL;
275
+
276
+ sig = git__calloc(1, sizeof(git_signature));
277
+ GITERR_CHECK_ALLOC(sig);
278
+
279
+ buf_end = buf + strlen(buf);
280
+ error = git_signature__parse(sig, &buf, buf_end, NULL, '\0');
281
+
282
+ if (error)
283
+ git__free(sig);
284
+ else
285
+ *out = sig;
286
+
287
+ return error;
288
+ }
289
+
265
290
  void git_signature__writebuf(git_buf *buf, const char *header, const git_signature *sig)
266
291
  {
267
292
  int offset, hours, mins;
@@ -33,6 +33,7 @@ int stransport_error(OSStatus ret)
33
33
  CFRelease(message);
34
34
  #else
35
35
  giterr_set(GITERR_NET, "SecureTransport error: OSStatus %d", (unsigned int)ret);
36
+ GIT_UNUSED(message);
36
37
  #endif
37
38
 
38
39
  return -1;
@@ -116,11 +117,13 @@ int stransport_certificate(git_cert **out, git_stream *stream)
116
117
  return 0;
117
118
  }
118
119
 
119
- int stransport_set_proxy(git_stream *stream, const char *proxy)
120
+ int stransport_set_proxy(
121
+ git_stream *stream,
122
+ const git_proxy_options *proxy_opts)
120
123
  {
121
124
  stransport_stream *st = (stransport_stream *) stream;
122
125
 
123
- return git_stream_set_proxy(st->io, proxy);
126
+ return git_stream_set_proxy(st->io, proxy_opts);
124
127
  }
125
128
 
126
129
  /*
@@ -35,14 +35,14 @@ GIT_INLINE(int) git_stream_supports_proxy(git_stream *st)
35
35
  return st->proxy_support;
36
36
  }
37
37
 
38
- GIT_INLINE(int) git_stream_set_proxy(git_stream *st, const char *proxy_url)
38
+ GIT_INLINE(int) git_stream_set_proxy(git_stream *st, const git_proxy_options *proxy_opts)
39
39
  {
40
40
  if (!st->proxy_support) {
41
41
  giterr_set(GITERR_INVALID, "proxy not supported on this stream");
42
42
  return -1;
43
43
  }
44
44
 
45
- return st->set_proxy(st, proxy_url);
45
+ return st->set_proxy(st, proxy_opts);
46
46
  }
47
47
 
48
48
  GIT_INLINE(ssize_t) git_stream_read(git_stream *st, void *data, size_t len)
@@ -80,7 +80,8 @@ static kh_inline int str_equal_no_trailing_slash(const char *a, const char *b)
80
80
  if (blen > 0 && b[blen - 1] == '/')
81
81
  blen--;
82
82
 
83
- return (alen == blen && strncmp(a, b, alen) == 0);
83
+ return (alen == 0 && blen == 0) ||
84
+ (alen == blen && strncmp(a, b, alen) == 0);
84
85
  }
85
86
 
86
87
  __KHASH_IMPL(
@@ -1416,7 +1417,7 @@ static int submodule_update_head(git_submodule *submodule)
1416
1417
  git_tree_entry_bypath(&te, head, submodule->path) < 0)
1417
1418
  giterr_clear();
1418
1419
  else
1419
- submodule_update_from_head_data(submodule, te->attr, &te->oid);
1420
+ submodule_update_from_head_data(submodule, te->attr, git_tree_entry_id(te));
1420
1421
 
1421
1422
  git_tree_entry_free(te);
1422
1423
  git_tree_free(head);
@@ -137,8 +137,14 @@ static int tag_parse(git_tag *tag, const char *buffer, const char *buffer_end)
137
137
 
138
138
  tag->message = NULL;
139
139
  if (buffer < buffer_end) {
140
- if( *buffer != '\n' )
141
- return tag_error("No new line before message");
140
+ /* If we're not at the end of the header, search for it */
141
+ if( *buffer != '\n' ) {
142
+ search = strstr(buffer, "\n\n");
143
+ if (search)
144
+ buffer = search + 1;
145
+ else
146
+ return tag_error("tag contains no message");
147
+ }
142
148
 
143
149
  text_len = buffer_end - ++buffer;
144
150
 
@@ -555,10 +555,40 @@ static int write_chunk(git_stream *io, const char *buffer, size_t len)
555
555
  return 0;
556
556
  }
557
557
 
558
+ static int apply_proxy_config(http_subtransport *t)
559
+ {
560
+ int error;
561
+ git_proxy_t proxy_type;
562
+
563
+ if (!git_stream_supports_proxy(t->io))
564
+ return 0;
565
+
566
+ proxy_type = t->owner->proxy.type;
567
+
568
+ if (proxy_type == GIT_PROXY_NONE)
569
+ return 0;
570
+
571
+ if (proxy_type == GIT_PROXY_AUTO) {
572
+ char *url;
573
+ git_proxy_options opts = GIT_PROXY_OPTIONS_INIT;
574
+
575
+ if ((error = git_remote__get_http_proxy(t->owner->owner, !!t->connection_data.use_ssl, &url)) < 0)
576
+ return error;
577
+
578
+ opts.type = GIT_PROXY_SPECIFIED;
579
+ opts.url = url;
580
+ error = git_stream_set_proxy(t->io, &opts);
581
+ git__free(url);
582
+
583
+ return error;
584
+ }
585
+
586
+ return git_stream_set_proxy(t->io, &t->owner->proxy);
587
+ }
588
+
558
589
  static int http_connect(http_subtransport *t)
559
590
  {
560
591
  int error;
561
- char *proxy_url;
562
592
 
563
593
  if (t->connected &&
564
594
  http_should_keep_alive(&t->parser) &&
@@ -586,14 +616,7 @@ static int http_connect(http_subtransport *t)
586
616
 
587
617
  GITERR_CHECK_VERSION(t->io, GIT_STREAM_VERSION, "git_stream");
588
618
 
589
- if (git_stream_supports_proxy(t->io) &&
590
- !git_remote__get_http_proxy(t->owner->owner, !!t->connection_data.use_ssl, &proxy_url)) {
591
- error = git_stream_set_proxy(t->io, proxy_url);
592
- git__free(proxy_url);
593
-
594
- if (error < 0)
595
- return error;
596
- }
619
+ apply_proxy_config(t);
597
620
 
598
621
  error = git_stream_connect(t->io);
599
622
 
@@ -25,6 +25,7 @@
25
25
  #include "odb.h"
26
26
  #include "push.h"
27
27
  #include "remote.h"
28
+ #include "proxy.h"
28
29
 
29
30
  typedef struct {
30
31
  git_transport parent;
@@ -199,6 +200,7 @@ static int local_connect(
199
200
  const char *url,
200
201
  git_cred_acquire_cb cred_acquire_cb,
201
202
  void *cred_acquire_payload,
203
+ const git_proxy_options *proxy,
202
204
  int direction, int flags)
203
205
  {
204
206
  git_repository *repo;
@@ -209,6 +211,7 @@ static int local_connect(
209
211
 
210
212
  GIT_UNUSED(cred_acquire_cb);
211
213
  GIT_UNUSED(cred_acquire_payload);
214
+ GIT_UNUSED(proxy);
212
215
 
213
216
  if (t->connected)
214
217
  return 0;
@@ -439,7 +442,7 @@ static int local_push(
439
442
 
440
443
  if (!url || t->parent.close(&t->parent) < 0 ||
441
444
  t->parent.connect(&t->parent, url,
442
- NULL, NULL, GIT_DIRECTION_PUSH, flags))
445
+ NULL, NULL, NULL, GIT_DIRECTION_PUSH, flags))
443
446
  goto on_error;
444
447
  }
445
448
 
@@ -8,6 +8,7 @@
8
8
  #include "smart.h"
9
9
  #include "refs.h"
10
10
  #include "refspec.h"
11
+ #include "proxy.h"
11
12
 
12
13
  static int git_smart__recv_cb(gitno_buffer *buf)
13
14
  {
@@ -199,6 +200,7 @@ static int git_smart__connect(
199
200
  const char *url,
200
201
  git_cred_acquire_cb cred_acquire_cb,
201
202
  void *cred_acquire_payload,
203
+ const git_proxy_options *proxy,
202
204
  int direction,
203
205
  int flags)
204
206
  {
@@ -216,6 +218,9 @@ static int git_smart__connect(
216
218
  t->url = git__strdup(url);
217
219
  GITERR_CHECK_ALLOC(t->url);
218
220
 
221
+ if (git_proxy_options_dup(&t->proxy, proxy) < 0)
222
+ return -1;
223
+
219
224
  t->direction = direction;
220
225
  t->flags = flags;
221
226
  t->cred_acquire_cb = cred_acquire_cb;
@@ -439,6 +444,7 @@ static void git_smart__free(git_transport *transport)
439
444
  git_pkt_free(p);
440
445
 
441
446
  git_vector_free(refs);
447
+ git__free((char *)t->proxy.url);
442
448
 
443
449
  git_strarray_free(&t->custom_headers);
444
450
 
@@ -133,6 +133,7 @@ typedef struct {
133
133
  char *url;
134
134
  git_cred_acquire_cb cred_acquire_cb;
135
135
  void *cred_acquire_payload;
136
+ git_proxy_options proxy;
136
137
  int direction;
137
138
  int flags;
138
139
  git_transport_message_cb progress_cb;
@@ -721,18 +721,39 @@ static int add_push_report_pkt(git_push *push, git_pkt *pkt)
721
721
  return 0;
722
722
  }
723
723
 
724
- static int add_push_report_sideband_pkt(git_push *push, git_pkt_data *data_pkt)
724
+ static int add_push_report_sideband_pkt(git_push *push, git_pkt_data *data_pkt, git_buf *data_pkt_buf)
725
725
  {
726
726
  git_pkt *pkt;
727
- const char *line = data_pkt->data, *line_end;
728
- size_t line_len = data_pkt->len;
727
+ const char *line, *line_end;
728
+ size_t line_len;
729
729
  int error;
730
+ int reading_from_buf = data_pkt_buf->size > 0;
731
+
732
+ if (reading_from_buf) {
733
+ /* We had an existing partial packet, so add the new
734
+ * packet to the buffer and parse the whole thing */
735
+ git_buf_put(data_pkt_buf, data_pkt->data, data_pkt->len);
736
+ line = data_pkt_buf->ptr;
737
+ line_len = data_pkt_buf->size;
738
+ }
739
+ else {
740
+ line = data_pkt->data;
741
+ line_len = data_pkt->len;
742
+ }
730
743
 
731
744
  while (line_len > 0) {
732
745
  error = git_pkt_parse_line(&pkt, line, &line_end, line_len);
733
746
 
734
- if (error < 0)
735
- return error;
747
+ if (error == GIT_EBUFS) {
748
+ /* Buffer the data when the inner packet is split
749
+ * across multiple sideband packets */
750
+ if (!reading_from_buf)
751
+ git_buf_put(data_pkt_buf, line, line_len);
752
+ error = 0;
753
+ goto done;
754
+ }
755
+ else if (error < 0)
756
+ goto done;
736
757
 
737
758
  /* Advance in the buffer */
738
759
  line_len -= (line_end - line);
@@ -743,10 +764,15 @@ static int add_push_report_sideband_pkt(git_push *push, git_pkt_data *data_pkt)
743
764
  git_pkt_free(pkt);
744
765
 
745
766
  if (error < 0 && error != GIT_ITEROVER)
746
- return error;
767
+ goto done;
747
768
  }
748
769
 
749
- return 0;
770
+ error = 0;
771
+
772
+ done:
773
+ if (reading_from_buf)
774
+ git_buf_consume(data_pkt_buf, line_end);
775
+ return error;
750
776
  }
751
777
 
752
778
  static int parse_report(transport_smart *transport, git_push *push)
@@ -755,6 +781,7 @@ static int parse_report(transport_smart *transport, git_push *push)
755
781
  const char *line_end = NULL;
756
782
  gitno_buffer *buf = &transport->buffer;
757
783
  int error, recvd;
784
+ git_buf data_pkt_buf = GIT_BUF_INIT;
758
785
 
759
786
  for (;;) {
760
787
  if (buf->offset > 0)
@@ -763,16 +790,21 @@ static int parse_report(transport_smart *transport, git_push *push)
763
790
  else
764
791
  error = GIT_EBUFS;
765
792
 
766
- if (error < 0 && error != GIT_EBUFS)
767
- return -1;
793
+ if (error < 0 && error != GIT_EBUFS) {
794
+ error = -1;
795
+ goto done;
796
+ }
768
797
 
769
798
  if (error == GIT_EBUFS) {
770
- if ((recvd = gitno_recv(buf)) < 0)
771
- return recvd;
799
+ if ((recvd = gitno_recv(buf)) < 0) {
800
+ error = recvd;
801
+ goto done;
802
+ }
772
803
 
773
804
  if (recvd == 0) {
774
805
  giterr_set(GITERR_NET, "early EOF");
775
- return GIT_EEOF;
806
+ error = GIT_EEOF;
807
+ goto done;
776
808
  }
777
809
  continue;
778
810
  }
@@ -784,7 +816,7 @@ static int parse_report(transport_smart *transport, git_push *push)
784
816
  switch (pkt->type) {
785
817
  case GIT_PKT_DATA:
786
818
  /* This is a sideband packet which contains other packets */
787
- error = add_push_report_sideband_pkt(push, (git_pkt_data *)pkt);
819
+ error = add_push_report_sideband_pkt(push, (git_pkt_data *)pkt, &data_pkt_buf);
788
820
  break;
789
821
  case GIT_PKT_ERR:
790
822
  giterr_set(GITERR_NET, "report-status: Error reported: %s",
@@ -805,12 +837,24 @@ static int parse_report(transport_smart *transport, git_push *push)
805
837
  git_pkt_free(pkt);
806
838
 
807
839
  /* add_push_report_pkt returns GIT_ITEROVER when it receives a flush */
808
- if (error == GIT_ITEROVER)
809
- return 0;
840
+ if (error == GIT_ITEROVER) {
841
+ error = 0;
842
+ if (data_pkt_buf.size > 0) {
843
+ /* If there was data remaining in the pack data buffer,
844
+ * then the server sent a partial pkt-line */
845
+ giterr_set(GITERR_NET, "Incomplete pack data pkt-line");
846
+ error = GIT_ERROR;
847
+ }
848
+ goto done;
849
+ }
810
850
 
811
- if (error < 0)
812
- return error;
851
+ if (error < 0) {
852
+ goto done;
853
+ }
813
854
  }
855
+ done:
856
+ git_buf_free(&data_pkt_buf);
857
+ return error;
814
858
  }
815
859
 
816
860
  static int add_ref_from_push_spec(git_vector *refs, push_spec *push_spec)
@@ -91,13 +91,39 @@ typedef struct {
91
91
  git_smart_subtransport parent;
92
92
  transport_smart *owner;
93
93
  gitno_connection_data connection_data;
94
+ gitno_connection_data proxy_connection_data;
94
95
  git_cred *cred;
95
96
  git_cred *url_cred;
97
+ git_cred *proxy_cred;
96
98
  int auth_mechanism;
97
99
  HINTERNET session;
98
100
  HINTERNET connection;
99
101
  } winhttp_subtransport;
100
102
 
103
+ static int apply_basic_credential_proxy(HINTERNET request, git_cred *cred)
104
+ {
105
+ git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred;
106
+ wchar_t *user, *pass;
107
+ int error;
108
+
109
+ if ((error = git__utf8_to_16_alloc(&user, c->username)) < 0)
110
+ return error;
111
+
112
+ if ((error = git__utf8_to_16_alloc(&pass, c->password)) < 0)
113
+ return error;
114
+
115
+ if (!WinHttpSetCredentials(request, WINHTTP_AUTH_TARGET_PROXY, WINHTTP_AUTH_SCHEME_BASIC,
116
+ user, pass, NULL)) {
117
+ giterr_set(GITERR_OS, "failed to set proxy auth");
118
+ error = -1;
119
+ }
120
+
121
+ git__free(user);
122
+ git__free(pass);
123
+
124
+ return error;
125
+ }
126
+
101
127
  static int apply_basic_credential(HINTERNET request, git_cred *cred)
102
128
  {
103
129
  git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred;
@@ -271,6 +297,37 @@ static void winhttp_stream_close(winhttp_stream *s)
271
297
  s->sent_request = 0;
272
298
  }
273
299
 
300
+ /**
301
+ * Extract the url and password from a URL. The outputs are pointers
302
+ * into the input.
303
+ */
304
+ static int userpass_from_url(wchar_t **user, int *user_len,
305
+ wchar_t **pass, int *pass_len,
306
+ const wchar_t *url, int url_len)
307
+ {
308
+ URL_COMPONENTS components = { 0 };
309
+
310
+ components.dwStructSize = sizeof(components);
311
+ /* These tell WinHttpCrackUrl that we're interested in the fields */
312
+ components.dwUserNameLength = 1;
313
+ components.dwPasswordLength = 1;
314
+
315
+ if (!WinHttpCrackUrl(url, url_len, 0, &components)) {
316
+ giterr_set(GITERR_OS, "failed to extract user/pass from url");
317
+ return -1;
318
+ }
319
+
320
+ *user = components.lpszUserName;
321
+ *user_len = components.dwUserNameLength;
322
+ *pass = components.lpszPassword;
323
+ *pass_len = components.dwPasswordLength;
324
+
325
+ return 0;
326
+ }
327
+
328
+ #define SCHEME_HTTP "http://"
329
+ #define SCHEME_HTTPS "https://"
330
+
274
331
  static int winhttp_stream_connect(winhttp_stream *s)
275
332
  {
276
333
  winhttp_subtransport *t = OWNING_SUBTRANSPORT(s);
@@ -284,6 +341,7 @@ static int winhttp_stream_connect(winhttp_stream *s)
284
341
  int default_timeout = TIMEOUT_INFINITE;
285
342
  int default_connect_timeout = DEFAULT_CONNECT_TIMEOUT;
286
343
  size_t i;
344
+ const git_proxy_options *proxy_opts;
287
345
 
288
346
  /* Prepare URL */
289
347
  git_buf_printf(&buf, "%s%s", t->connection_data.path, s->service_url);
@@ -317,26 +375,59 @@ static int winhttp_stream_connect(winhttp_stream *s)
317
375
  goto on_error;
318
376
  }
319
377
 
320
- /* Set proxy if necessary */
321
- if (git_remote__get_http_proxy(t->owner->owner, !!t->connection_data.use_ssl, &proxy_url) < 0)
322
- goto on_error;
378
+ proxy_opts = &t->owner->proxy;
379
+ if (proxy_opts->type == GIT_PROXY_AUTO) {
380
+ /* Set proxy if necessary */
381
+ if (git_remote__get_http_proxy(t->owner->owner, !!t->connection_data.use_ssl, &proxy_url) < 0)
382
+ goto on_error;
383
+ }
384
+ else if (proxy_opts->type == GIT_PROXY_SPECIFIED) {
385
+ proxy_url = git__strdup(proxy_opts->url);
386
+ GITERR_CHECK_ALLOC(proxy_url);
387
+ }
323
388
 
324
389
  if (proxy_url) {
390
+ git_buf processed_url = GIT_BUF_INIT;
325
391
  WINHTTP_PROXY_INFO proxy_info;
326
392
  wchar_t *proxy_wide;
327
393
 
328
- /* Convert URL to wide characters */
329
- int proxy_wide_len = git__utf8_to_16_alloc(&proxy_wide, proxy_url);
394
+ if (!git__prefixcmp(proxy_url, SCHEME_HTTP)) {
395
+ t->proxy_connection_data.use_ssl = false;
396
+ } else if (!git__prefixcmp(proxy_url, SCHEME_HTTPS)) {
397
+ t->proxy_connection_data.use_ssl = true;
398
+ } else {
399
+ giterr_set(GITERR_NET, "invalid URL: '%s'", proxy_url);
400
+ return -1;
401
+ }
330
402
 
331
- if (proxy_wide_len < 0) {
332
- giterr_set(GITERR_OS, "Failed to convert string to wide form");
403
+ if ((error = gitno_extract_url_parts(&t->proxy_connection_data.host, &t->proxy_connection_data.port, NULL,
404
+ &t->proxy_connection_data.user, &t->proxy_connection_data.pass, proxy_url, NULL)) < 0)
405
+ goto on_error;
406
+
407
+ if (t->proxy_connection_data.user && t->proxy_connection_data.pass) {
408
+ if ((error = git_cred_userpass_plaintext_new(&t->proxy_cred, t->proxy_connection_data.user, t->proxy_connection_data.pass)) < 0)
409
+ goto on_error;
410
+ }
411
+
412
+ if (t->proxy_connection_data.use_ssl)
413
+ git_buf_PUTS(&processed_url, SCHEME_HTTPS);
414
+ else
415
+ git_buf_PUTS(&processed_url, SCHEME_HTTP);
416
+
417
+ git_buf_puts(&processed_url, t->proxy_connection_data.host);
418
+ if (t->proxy_connection_data.port)
419
+ git_buf_printf(&processed_url, ":%s", t->proxy_connection_data.port);
420
+
421
+ if (git_buf_oom(&processed_url)) {
422
+ giterr_set_oom();
423
+ error = -1;
333
424
  goto on_error;
334
425
  }
335
426
 
336
- /* Strip any trailing forward slash on the proxy URL;
337
- * WinHTTP doesn't like it if one is present */
338
- if (proxy_wide_len > 1 && L'/' == proxy_wide[proxy_wide_len - 2])
339
- proxy_wide[proxy_wide_len - 2] = L'\0';
427
+ /* Convert URL to wide characters */
428
+ if ((error = git__utf8_to_16_alloc(&proxy_wide, processed_url.ptr)) < 0)
429
+ goto on_error;
430
+
340
431
 
341
432
  proxy_info.dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
342
433
  proxy_info.lpszProxy = proxy_wide;
@@ -352,6 +443,14 @@ static int winhttp_stream_connect(winhttp_stream *s)
352
443
  }
353
444
 
354
445
  git__free(proxy_wide);
446
+
447
+ if (t->proxy_cred) {
448
+ if (t->proxy_cred->credtype == GIT_CREDTYPE_USERPASS_PLAINTEXT) {
449
+ if ((error = apply_basic_credential_proxy(s->request, t->proxy_cred)) < 0)
450
+ goto on_error;
451
+ }
452
+ }
453
+
355
454
  }
356
455
 
357
456
  /* Disable WinHTTP redirects so we can handle them manually. Why, you ask?
@@ -919,6 +1018,26 @@ replay:
919
1018
  goto replay;
920
1019
  }
921
1020
 
1021
+ /* Handle proxy authentication failures */
1022
+ if (status_code == HTTP_STATUS_PROXY_AUTH_REQ) {
1023
+ int allowed_types;
1024
+
1025
+ if (parse_unauthorized_response(s->request, &allowed_types, &t->auth_mechanism) < 0)
1026
+ return -1;
1027
+
1028
+ /* TODO: extract the username from the url, no payload? */
1029
+ if (t->owner->proxy.credentials) {
1030
+ int cred_error = 1;
1031
+ cred_error = t->owner->proxy.credentials(&t->proxy_cred, t->owner->proxy.url, NULL, allowed_types, NULL);
1032
+
1033
+ if (cred_error < 0)
1034
+ return cred_error;
1035
+ }
1036
+
1037
+ winhttp_stream_close(s);
1038
+ goto replay;
1039
+ }
1040
+
922
1041
  /* Handle authentication failures */
923
1042
  if (HTTP_STATUS_DENIED == status_code && get_verb == s->verb) {
924
1043
  int allowed_types;