rugged 0.17.0.b7 → 0.18.0.b1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (310) hide show
  1. data/LICENSE +1 -1
  2. data/README.md +88 -32
  3. data/ext/rugged/extconf.rb +4 -2
  4. data/ext/rugged/rugged.c +72 -10
  5. data/ext/rugged/rugged.h +14 -10
  6. data/ext/rugged/rugged_blob.c +8 -10
  7. data/ext/rugged/rugged_branch.c +11 -14
  8. data/ext/rugged/rugged_commit.c +31 -24
  9. data/ext/rugged/rugged_config.c +2 -2
  10. data/ext/rugged/rugged_index.c +133 -198
  11. data/ext/rugged/rugged_note.c +372 -0
  12. data/ext/rugged/rugged_object.c +50 -22
  13. data/ext/rugged/rugged_reference.c +122 -130
  14. data/ext/rugged/rugged_remote.c +72 -29
  15. data/ext/rugged/rugged_repo.c +402 -20
  16. data/ext/rugged/rugged_revwalk.c +7 -3
  17. data/ext/rugged/rugged_settings.c +110 -0
  18. data/ext/rugged/rugged_signature.c +23 -7
  19. data/ext/rugged/rugged_tag.c +32 -16
  20. data/ext/rugged/rugged_tree.c +44 -15
  21. data/lib/rugged.rb +1 -0
  22. data/lib/rugged/index.rb +8 -0
  23. data/lib/rugged/remote.rb +13 -0
  24. data/lib/rugged/repository.rb +3 -3
  25. data/lib/rugged/version.rb +1 -1
  26. data/test/blob_test.rb +13 -15
  27. data/test/branch_test.rb +32 -67
  28. data/test/commit_test.rb +50 -12
  29. data/test/config_test.rb +12 -11
  30. data/test/coverage/HEAD.json +1 -1
  31. data/test/coverage/cover.rb +40 -21
  32. data/test/errors_test.rb +34 -0
  33. data/test/fixtures/alternate/objects/14/6ae76773c91e3b1d00cf7a338ec55ae58297e2 +0 -0
  34. data/test/fixtures/alternate/objects/14/9c32d47e99d0a3572ff1e70a2e0051bbf347a9 +0 -0
  35. data/test/fixtures/alternate/objects/14/fb3108588f9421bf764041e5e3ac305eb6277f +0 -0
  36. data/test/fixtures/testrepo.git/logs/refs/notes/commits +1 -0
  37. data/test/fixtures/testrepo.git/objects/44/1034f860c1d5d90e4188d11ae0d325176869a8 +1 -0
  38. data/test/fixtures/testrepo.git/objects/60/d415052a33de2150bf68757f6461df4f563ae4 +0 -0
  39. data/test/fixtures/testrepo.git/objects/68/8a8f4ef7496901d15322972f96e212a9e466cc +1 -0
  40. data/test/fixtures/testrepo.git/objects/94/eca2de348d5f672faf56b0decafa5937e3235e +0 -0
  41. data/test/fixtures/testrepo.git/objects/9b/7384fe1676186192842f5d3e129457b62db9e3 +0 -0
  42. data/test/fixtures/testrepo.git/objects/b7/4713326bc972cc15751ed504dca6f6f3b91f7a +3 -0
  43. data/test/fixtures/testrepo.git/refs/notes/commits +1 -0
  44. data/test/index_test.rb +65 -69
  45. data/test/lib_test.rb +76 -11
  46. data/test/note_test.rb +158 -0
  47. data/test/object_test.rb +8 -11
  48. data/test/reference_test.rb +77 -85
  49. data/test/remote_test.rb +86 -8
  50. data/test/repo_pack_test.rb +9 -7
  51. data/test/repo_reset_test.rb +80 -0
  52. data/test/repo_test.rb +176 -53
  53. data/test/tag_test.rb +44 -7
  54. data/test/test_helper.rb +63 -35
  55. data/test/tree_test.rb +34 -13
  56. data/test/walker_test.rb +14 -14
  57. data/vendor/libgit2/Makefile.embed +1 -1
  58. data/vendor/libgit2/deps/http-parser/http_parser.c +974 -578
  59. data/vendor/libgit2/deps/http-parser/http_parser.h +106 -70
  60. data/vendor/libgit2/deps/regex/regcomp.c +7 -6
  61. data/vendor/libgit2/deps/regex/regex_internal.c +1 -1
  62. data/vendor/libgit2/deps/regex/regex_internal.h +12 -3
  63. data/vendor/libgit2/deps/regex/regexec.c +5 -5
  64. data/vendor/libgit2/include/git2.h +5 -1
  65. data/vendor/libgit2/include/git2/attr.h +4 -2
  66. data/vendor/libgit2/include/git2/blob.h +39 -12
  67. data/vendor/libgit2/include/git2/branch.h +123 -35
  68. data/vendor/libgit2/include/git2/checkout.h +206 -48
  69. data/vendor/libgit2/include/git2/clone.h +72 -27
  70. data/vendor/libgit2/include/git2/commit.h +20 -17
  71. data/vendor/libgit2/include/git2/common.h +67 -1
  72. data/vendor/libgit2/include/git2/config.h +81 -60
  73. data/vendor/libgit2/include/git2/cred_helpers.h +53 -0
  74. data/vendor/libgit2/include/git2/diff.h +459 -150
  75. data/vendor/libgit2/include/git2/errors.h +9 -1
  76. data/vendor/libgit2/include/git2/graph.h +41 -0
  77. data/vendor/libgit2/include/git2/ignore.h +7 -6
  78. data/vendor/libgit2/include/git2/index.h +323 -97
  79. data/vendor/libgit2/include/git2/indexer.h +27 -59
  80. data/vendor/libgit2/include/git2/inttypes.h +4 -0
  81. data/vendor/libgit2/include/git2/merge.h +13 -3
  82. data/vendor/libgit2/include/git2/message.h +14 -8
  83. data/vendor/libgit2/include/git2/net.h +9 -7
  84. data/vendor/libgit2/include/git2/notes.h +88 -29
  85. data/vendor/libgit2/include/git2/object.h +16 -6
  86. data/vendor/libgit2/include/git2/odb.h +80 -17
  87. data/vendor/libgit2/include/git2/odb_backend.h +47 -11
  88. data/vendor/libgit2/include/git2/oid.h +26 -17
  89. data/vendor/libgit2/include/git2/pack.h +62 -8
  90. data/vendor/libgit2/include/git2/push.h +131 -0
  91. data/vendor/libgit2/include/git2/refdb.h +103 -0
  92. data/vendor/libgit2/include/git2/refdb_backend.h +109 -0
  93. data/vendor/libgit2/include/git2/reflog.h +30 -21
  94. data/vendor/libgit2/include/git2/refs.h +215 -193
  95. data/vendor/libgit2/include/git2/refspec.h +22 -2
  96. data/vendor/libgit2/include/git2/remote.h +158 -37
  97. data/vendor/libgit2/include/git2/repository.h +150 -31
  98. data/vendor/libgit2/include/git2/reset.h +43 -9
  99. data/vendor/libgit2/include/git2/revparse.h +48 -4
  100. data/vendor/libgit2/include/git2/revwalk.h +25 -10
  101. data/vendor/libgit2/include/git2/signature.h +20 -12
  102. data/vendor/libgit2/include/git2/stash.h +121 -0
  103. data/vendor/libgit2/include/git2/status.h +122 -53
  104. data/vendor/libgit2/include/git2/strarray.h +17 -11
  105. data/vendor/libgit2/include/git2/submodule.h +42 -7
  106. data/vendor/libgit2/include/git2/tag.h +72 -59
  107. data/vendor/libgit2/include/git2/threads.h +4 -2
  108. data/vendor/libgit2/include/git2/trace.h +68 -0
  109. data/vendor/libgit2/include/git2/transport.h +328 -0
  110. data/vendor/libgit2/include/git2/tree.h +149 -120
  111. data/vendor/libgit2/include/git2/types.h +13 -12
  112. data/vendor/libgit2/include/git2/version.h +3 -3
  113. data/vendor/libgit2/src/amiga/map.c +2 -2
  114. data/vendor/libgit2/src/attr.c +58 -48
  115. data/vendor/libgit2/src/attr.h +4 -18
  116. data/vendor/libgit2/src/attr_file.c +30 -6
  117. data/vendor/libgit2/src/attr_file.h +6 -8
  118. data/vendor/libgit2/src/attrcache.h +24 -0
  119. data/vendor/libgit2/src/blob.c +30 -7
  120. data/vendor/libgit2/src/blob.h +1 -1
  121. data/vendor/libgit2/src/branch.c +361 -68
  122. data/vendor/libgit2/src/branch.h +17 -0
  123. data/vendor/libgit2/src/bswap.h +1 -1
  124. data/vendor/libgit2/src/buf_text.c +291 -0
  125. data/vendor/libgit2/src/buf_text.h +122 -0
  126. data/vendor/libgit2/src/buffer.c +27 -101
  127. data/vendor/libgit2/src/buffer.h +54 -39
  128. data/vendor/libgit2/src/cache.c +15 -6
  129. data/vendor/libgit2/src/cache.h +1 -1
  130. data/vendor/libgit2/src/cc-compat.h +3 -1
  131. data/vendor/libgit2/src/checkout.c +1165 -222
  132. data/vendor/libgit2/src/checkout.h +24 -0
  133. data/vendor/libgit2/src/clone.c +171 -86
  134. data/vendor/libgit2/src/commit.c +44 -45
  135. data/vendor/libgit2/src/commit.h +3 -3
  136. data/vendor/libgit2/src/commit_list.c +194 -0
  137. data/vendor/libgit2/src/commit_list.h +49 -0
  138. data/vendor/libgit2/src/common.h +44 -10
  139. data/vendor/libgit2/src/compress.c +1 -1
  140. data/vendor/libgit2/src/compress.h +1 -1
  141. data/vendor/libgit2/src/config.c +211 -124
  142. data/vendor/libgit2/src/config.h +23 -4
  143. data/vendor/libgit2/src/config_cache.c +2 -2
  144. data/vendor/libgit2/src/config_file.c +129 -53
  145. data/vendor/libgit2/src/config_file.h +10 -8
  146. data/vendor/libgit2/src/crlf.c +66 -67
  147. data/vendor/libgit2/src/date.c +12 -12
  148. data/vendor/libgit2/src/delta-apply.c +14 -1
  149. data/vendor/libgit2/src/delta-apply.h +18 -1
  150. data/vendor/libgit2/src/delta.c +40 -107
  151. data/vendor/libgit2/src/delta.h +19 -17
  152. data/vendor/libgit2/src/diff.c +347 -496
  153. data/vendor/libgit2/src/diff.h +27 -1
  154. data/vendor/libgit2/src/diff_output.c +564 -249
  155. data/vendor/libgit2/src/diff_output.h +15 -8
  156. data/vendor/libgit2/src/diff_tform.c +687 -0
  157. data/vendor/libgit2/src/errors.c +27 -36
  158. data/vendor/libgit2/src/fetch.c +13 -351
  159. data/vendor/libgit2/src/fetch.h +13 -3
  160. data/vendor/libgit2/src/fetchhead.c +295 -0
  161. data/vendor/libgit2/src/fetchhead.h +34 -0
  162. data/vendor/libgit2/src/filebuf.c +42 -15
  163. data/vendor/libgit2/src/filebuf.h +4 -2
  164. data/vendor/libgit2/src/fileops.c +466 -113
  165. data/vendor/libgit2/src/fileops.h +154 -28
  166. data/vendor/libgit2/src/filter.c +3 -75
  167. data/vendor/libgit2/src/filter.h +1 -29
  168. data/vendor/libgit2/src/fnmatch.c +1 -1
  169. data/vendor/libgit2/src/fnmatch.h +1 -1
  170. data/vendor/libgit2/src/global.c +54 -10
  171. data/vendor/libgit2/src/global.h +10 -1
  172. data/vendor/libgit2/src/graph.c +178 -0
  173. data/vendor/libgit2/src/hash.c +25 -52
  174. data/vendor/libgit2/src/hash.h +21 -9
  175. data/vendor/libgit2/src/{sha1/sha1.c → hash/hash_generic.c} +20 -12
  176. data/vendor/libgit2/src/hash/hash_generic.h +24 -0
  177. data/vendor/libgit2/src/hash/hash_openssl.h +45 -0
  178. data/vendor/libgit2/src/hash/hash_win32.c +291 -0
  179. data/vendor/libgit2/src/hash/hash_win32.h +140 -0
  180. data/vendor/libgit2/src/hashsig.c +368 -0
  181. data/vendor/libgit2/src/hashsig.h +72 -0
  182. data/vendor/libgit2/src/ignore.c +22 -15
  183. data/vendor/libgit2/src/ignore.h +6 -1
  184. data/vendor/libgit2/src/index.c +770 -171
  185. data/vendor/libgit2/src/index.h +13 -5
  186. data/vendor/libgit2/src/indexer.c +286 -431
  187. data/vendor/libgit2/src/iterator.c +854 -466
  188. data/vendor/libgit2/src/iterator.h +134 -109
  189. data/vendor/libgit2/src/map.h +1 -1
  190. data/vendor/libgit2/src/merge.c +296 -0
  191. data/vendor/libgit2/src/merge.h +22 -0
  192. data/vendor/libgit2/src/message.c +1 -1
  193. data/vendor/libgit2/src/message.h +1 -1
  194. data/vendor/libgit2/src/mwindow.c +35 -30
  195. data/vendor/libgit2/src/mwindow.h +2 -2
  196. data/vendor/libgit2/src/netops.c +162 -98
  197. data/vendor/libgit2/src/netops.h +50 -15
  198. data/vendor/libgit2/src/notes.c +109 -58
  199. data/vendor/libgit2/src/notes.h +2 -1
  200. data/vendor/libgit2/src/object.c +46 -57
  201. data/vendor/libgit2/src/object.h +1 -8
  202. data/vendor/libgit2/src/odb.c +151 -40
  203. data/vendor/libgit2/src/odb.h +5 -1
  204. data/vendor/libgit2/src/odb_loose.c +4 -5
  205. data/vendor/libgit2/src/odb_pack.c +122 -80
  206. data/vendor/libgit2/src/offmap.h +65 -0
  207. data/vendor/libgit2/src/oid.c +12 -4
  208. data/vendor/libgit2/src/oidmap.h +1 -1
  209. data/vendor/libgit2/src/pack-objects.c +88 -61
  210. data/vendor/libgit2/src/pack-objects.h +8 -8
  211. data/vendor/libgit2/src/pack.c +293 -28
  212. data/vendor/libgit2/src/pack.h +49 -4
  213. data/vendor/libgit2/src/path.c +103 -14
  214. data/vendor/libgit2/src/path.h +23 -7
  215. data/vendor/libgit2/src/pathspec.c +168 -0
  216. data/vendor/libgit2/src/pathspec.h +40 -0
  217. data/vendor/libgit2/src/pool.c +29 -4
  218. data/vendor/libgit2/src/pool.h +8 -1
  219. data/vendor/libgit2/src/posix.c +26 -27
  220. data/vendor/libgit2/src/posix.h +2 -3
  221. data/vendor/libgit2/src/pqueue.c +23 -1
  222. data/vendor/libgit2/src/pqueue.h +23 -1
  223. data/vendor/libgit2/src/push.c +653 -0
  224. data/vendor/libgit2/src/push.h +51 -0
  225. data/vendor/libgit2/src/refdb.c +185 -0
  226. data/vendor/libgit2/src/refdb.h +46 -0
  227. data/vendor/libgit2/src/refdb_fs.c +1024 -0
  228. data/vendor/libgit2/src/refdb_fs.h +15 -0
  229. data/vendor/libgit2/src/reflog.c +77 -45
  230. data/vendor/libgit2/src/reflog.h +1 -3
  231. data/vendor/libgit2/src/refs.c +366 -1326
  232. data/vendor/libgit2/src/refs.h +22 -13
  233. data/vendor/libgit2/src/refspec.c +46 -7
  234. data/vendor/libgit2/src/refspec.h +11 -1
  235. data/vendor/libgit2/src/remote.c +758 -120
  236. data/vendor/libgit2/src/remote.h +10 -5
  237. data/vendor/libgit2/src/repo_template.h +6 -6
  238. data/vendor/libgit2/src/repository.c +315 -96
  239. data/vendor/libgit2/src/repository.h +5 -3
  240. data/vendor/libgit2/src/reset.c +99 -81
  241. data/vendor/libgit2/src/revparse.c +157 -84
  242. data/vendor/libgit2/src/revwalk.c +68 -470
  243. data/vendor/libgit2/src/revwalk.h +44 -0
  244. data/vendor/libgit2/src/sha1_lookup.c +1 -1
  245. data/vendor/libgit2/src/sha1_lookup.h +1 -1
  246. data/vendor/libgit2/src/signature.c +68 -200
  247. data/vendor/libgit2/src/signature.h +1 -1
  248. data/vendor/libgit2/src/stash.c +663 -0
  249. data/vendor/libgit2/src/status.c +101 -79
  250. data/vendor/libgit2/src/strmap.h +1 -1
  251. data/vendor/libgit2/src/submodule.c +67 -51
  252. data/vendor/libgit2/src/submodule.h +1 -1
  253. data/vendor/libgit2/src/tag.c +35 -29
  254. data/vendor/libgit2/src/tag.h +1 -1
  255. data/vendor/libgit2/src/thread-utils.c +1 -1
  256. data/vendor/libgit2/src/thread-utils.h +2 -2
  257. data/vendor/libgit2/src/trace.c +39 -0
  258. data/vendor/libgit2/src/trace.h +56 -0
  259. data/vendor/libgit2/src/transport.c +81 -34
  260. data/vendor/libgit2/src/transports/cred.c +60 -0
  261. data/vendor/libgit2/src/transports/cred_helpers.c +49 -0
  262. data/vendor/libgit2/src/transports/git.c +234 -127
  263. data/vendor/libgit2/src/transports/http.c +761 -433
  264. data/vendor/libgit2/src/transports/local.c +460 -64
  265. data/vendor/libgit2/src/transports/smart.c +345 -0
  266. data/vendor/libgit2/src/transports/smart.h +179 -0
  267. data/vendor/libgit2/src/{pkt.c → transports/smart_pkt.c} +131 -12
  268. data/vendor/libgit2/src/transports/smart_protocol.c +856 -0
  269. data/vendor/libgit2/src/transports/winhttp.c +1136 -0
  270. data/vendor/libgit2/src/tree-cache.c +2 -2
  271. data/vendor/libgit2/src/tree-cache.h +1 -1
  272. data/vendor/libgit2/src/tree.c +239 -166
  273. data/vendor/libgit2/src/tree.h +11 -2
  274. data/vendor/libgit2/src/tsort.c +39 -23
  275. data/vendor/libgit2/src/unix/map.c +1 -1
  276. data/vendor/libgit2/src/unix/posix.h +12 -2
  277. data/vendor/libgit2/src/unix/realpath.c +30 -0
  278. data/vendor/libgit2/src/util.c +250 -13
  279. data/vendor/libgit2/src/util.h +71 -14
  280. data/vendor/libgit2/src/vector.c +123 -60
  281. data/vendor/libgit2/src/vector.h +24 -22
  282. data/vendor/libgit2/src/win32/dir.c +1 -1
  283. data/vendor/libgit2/src/win32/dir.h +1 -1
  284. data/vendor/libgit2/src/win32/error.c +77 -0
  285. data/vendor/libgit2/src/win32/error.h +13 -0
  286. data/vendor/libgit2/src/win32/findfile.c +143 -54
  287. data/vendor/libgit2/src/win32/findfile.h +10 -6
  288. data/vendor/libgit2/src/win32/map.c +1 -1
  289. data/vendor/libgit2/src/win32/mingw-compat.h +1 -1
  290. data/vendor/libgit2/src/win32/msvc-compat.h +10 -1
  291. data/vendor/libgit2/src/win32/posix.h +10 -1
  292. data/vendor/libgit2/src/win32/posix_w32.c +132 -63
  293. data/vendor/libgit2/src/win32/precompiled.c +1 -1
  294. data/vendor/libgit2/src/win32/pthread.c +1 -1
  295. data/vendor/libgit2/src/win32/pthread.h +1 -1
  296. data/vendor/libgit2/src/win32/utf-conv.c +5 -5
  297. data/vendor/libgit2/src/win32/utf-conv.h +3 -3
  298. data/vendor/libgit2/src/win32/version.h +20 -0
  299. metadata +308 -252
  300. data/test/fixtures/testrepo.git/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904 +0 -0
  301. data/test/fixtures/testrepo.git/objects/7f/043268ea43ce18e3540acaabf9e090c91965b0 +0 -0
  302. data/test/fixtures/testrepo.git/objects/a3/e05719b428a2d0ed7a55c4ce53dcc5768c6d5e +0 -0
  303. data/test/index_test.rb~ +0 -218
  304. data/vendor/libgit2/src/pkt.h +0 -91
  305. data/vendor/libgit2/src/ppc/sha1.c +0 -70
  306. data/vendor/libgit2/src/ppc/sha1.h +0 -26
  307. data/vendor/libgit2/src/protocol.c +0 -110
  308. data/vendor/libgit2/src/protocol.h +0 -21
  309. data/vendor/libgit2/src/sha1.h +0 -33
  310. data/vendor/libgit2/src/transport.h +0 -148
@@ -0,0 +1,1136 @@
1
+ /*
2
+ * Copyright (C) the libgit2 contributors. All rights reserved.
3
+ *
4
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
5
+ * a Linking Exception. For full terms see the included COPYING file.
6
+ */
7
+
8
+ #ifdef GIT_WINHTTP
9
+
10
+ #include "git2.h"
11
+ #include "git2/transport.h"
12
+ #include "buffer.h"
13
+ #include "posix.h"
14
+ #include "netops.h"
15
+ #include "smart.h"
16
+ #include "remote.h"
17
+ #include "repository.h"
18
+
19
+ #include <winhttp.h>
20
+ #pragma comment(lib, "winhttp")
21
+
22
+ /* For UuidCreate */
23
+ #pragma comment(lib, "rpcrt4")
24
+
25
+ #define WIDEN2(s) L ## s
26
+ #define WIDEN(s) WIDEN2(s)
27
+
28
+ #define MAX_CONTENT_TYPE_LEN 100
29
+ #define WINHTTP_OPTION_PEERDIST_EXTENSION_STATE 109
30
+ #define CACHED_POST_BODY_BUF_SIZE 4096
31
+ #define UUID_LENGTH_CCH 32
32
+
33
+ static const char *prefix_http = "http://";
34
+ static const char *prefix_https = "https://";
35
+ static const char *upload_pack_service = "upload-pack";
36
+ static const char *upload_pack_ls_service_url = "/info/refs?service=git-upload-pack";
37
+ static const char *upload_pack_service_url = "/git-upload-pack";
38
+ static const char *receive_pack_service = "receive-pack";
39
+ static const char *receive_pack_ls_service_url = "/info/refs?service=git-receive-pack";
40
+ static const char *receive_pack_service_url = "/git-receive-pack";
41
+ static const wchar_t *get_verb = L"GET";
42
+ static const wchar_t *post_verb = L"POST";
43
+ static const wchar_t *pragma_nocache = L"Pragma: no-cache";
44
+ static const wchar_t *transfer_encoding = L"Transfer-Encoding: chunked";
45
+ static const int no_check_cert_flags = SECURITY_FLAG_IGNORE_CERT_CN_INVALID |
46
+ SECURITY_FLAG_IGNORE_CERT_DATE_INVALID |
47
+ SECURITY_FLAG_IGNORE_UNKNOWN_CA;
48
+
49
+ #define OWNING_SUBTRANSPORT(s) ((winhttp_subtransport *)(s)->parent.subtransport)
50
+
51
+ typedef enum {
52
+ GIT_WINHTTP_AUTH_BASIC = 1,
53
+ } winhttp_authmechanism_t;
54
+
55
+ typedef struct {
56
+ git_smart_subtransport_stream parent;
57
+ const char *service;
58
+ const char *service_url;
59
+ const wchar_t *verb;
60
+ HINTERNET request;
61
+ wchar_t *request_uri;
62
+ char *chunk_buffer;
63
+ unsigned chunk_buffer_len;
64
+ HANDLE post_body;
65
+ DWORD post_body_len;
66
+ unsigned sent_request : 1,
67
+ received_response : 1,
68
+ chunked : 1;
69
+ } winhttp_stream;
70
+
71
+ typedef struct {
72
+ git_smart_subtransport parent;
73
+ transport_smart *owner;
74
+ const char *path;
75
+ char *host;
76
+ char *port;
77
+ char *user_from_url;
78
+ char *pass_from_url;
79
+ git_cred *cred;
80
+ git_cred *url_cred;
81
+ int auth_mechanism;
82
+ HINTERNET session;
83
+ HINTERNET connection;
84
+ unsigned use_ssl : 1;
85
+ } winhttp_subtransport;
86
+
87
+ static int apply_basic_credential(HINTERNET request, git_cred *cred)
88
+ {
89
+ git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred;
90
+ git_buf buf = GIT_BUF_INIT, raw = GIT_BUF_INIT;
91
+ wchar_t *wide = NULL;
92
+ int error = -1, wide_len = 0;
93
+
94
+ git_buf_printf(&raw, "%s:%s", c->username, c->password);
95
+
96
+ if (git_buf_oom(&raw) ||
97
+ git_buf_puts(&buf, "Authorization: Basic ") < 0 ||
98
+ git_buf_put_base64(&buf, git_buf_cstr(&raw), raw.size) < 0)
99
+ goto on_error;
100
+
101
+ wide_len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
102
+ git_buf_cstr(&buf), -1, NULL, 0);
103
+
104
+ if (!wide_len) {
105
+ giterr_set(GITERR_OS, "Failed to measure string for wide conversion");
106
+ goto on_error;
107
+ }
108
+
109
+ wide = git__malloc(wide_len * sizeof(wchar_t));
110
+
111
+ if (!wide)
112
+ goto on_error;
113
+
114
+ if (!MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
115
+ git_buf_cstr(&buf), -1, wide, wide_len)) {
116
+ giterr_set(GITERR_OS, "Failed to convert string to wide form");
117
+ goto on_error;
118
+ }
119
+
120
+ if (!WinHttpAddRequestHeaders(request, wide, (ULONG) -1L, WINHTTP_ADDREQ_FLAG_ADD)) {
121
+ giterr_set(GITERR_OS, "Failed to add a header to the request");
122
+ goto on_error;
123
+ }
124
+
125
+ error = 0;
126
+
127
+ on_error:
128
+ /* We were dealing with plaintext passwords, so clean up after ourselves a bit. */
129
+ if (wide)
130
+ memset(wide, 0x0, wide_len * sizeof(wchar_t));
131
+
132
+ if (buf.size)
133
+ memset(buf.ptr, 0x0, buf.size);
134
+
135
+ if (raw.size)
136
+ memset(raw.ptr, 0x0, raw.size);
137
+
138
+ git__free(wide);
139
+ git_buf_free(&buf);
140
+ git_buf_free(&raw);
141
+ return error;
142
+ }
143
+
144
+ static int winhttp_stream_connect(winhttp_stream *s)
145
+ {
146
+ winhttp_subtransport *t = OWNING_SUBTRANSPORT(s);
147
+ git_buf buf = GIT_BUF_INIT;
148
+ char *proxy_url = NULL;
149
+ wchar_t ct[MAX_CONTENT_TYPE_LEN];
150
+ wchar_t *types[] = { L"*/*", NULL };
151
+ BOOL peerdist = FALSE;
152
+ int error = -1, wide_len;
153
+
154
+ /* Prepare URL */
155
+ git_buf_printf(&buf, "%s%s", t->path, s->service_url);
156
+
157
+ if (git_buf_oom(&buf))
158
+ return -1;
159
+
160
+ /* Convert URL to wide characters */
161
+ wide_len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
162
+ git_buf_cstr(&buf), -1, NULL, 0);
163
+
164
+ if (!wide_len) {
165
+ giterr_set(GITERR_OS, "Failed to measure string for wide conversion");
166
+ goto on_error;
167
+ }
168
+
169
+ s->request_uri = git__malloc(wide_len * sizeof(wchar_t));
170
+
171
+ if (!s->request_uri)
172
+ goto on_error;
173
+
174
+ if (!MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
175
+ git_buf_cstr(&buf), -1, s->request_uri, wide_len)) {
176
+ giterr_set(GITERR_OS, "Failed to convert string to wide form");
177
+ goto on_error;
178
+ }
179
+
180
+ /* Establish request */
181
+ s->request = WinHttpOpenRequest(
182
+ t->connection,
183
+ s->verb,
184
+ s->request_uri,
185
+ NULL,
186
+ WINHTTP_NO_REFERER,
187
+ types,
188
+ t->use_ssl ? WINHTTP_FLAG_SECURE : 0);
189
+
190
+ if (!s->request) {
191
+ giterr_set(GITERR_OS, "Failed to open request");
192
+ goto on_error;
193
+ }
194
+
195
+ /* Set proxy if necessary */
196
+ if (git_remote__get_http_proxy(t->owner->owner, t->use_ssl, &proxy_url) < 0)
197
+ goto on_error;
198
+
199
+ if (proxy_url) {
200
+ WINHTTP_PROXY_INFO proxy_info;
201
+ wchar_t *proxy_wide;
202
+
203
+ /* Convert URL to wide characters */
204
+ wide_len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
205
+ proxy_url, -1, NULL, 0);
206
+
207
+ if (!wide_len) {
208
+ giterr_set(GITERR_OS, "Failed to measure string for wide conversion");
209
+ goto on_error;
210
+ }
211
+
212
+ proxy_wide = git__malloc(wide_len * sizeof(wchar_t));
213
+
214
+ if (!proxy_wide)
215
+ goto on_error;
216
+
217
+ if (!MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
218
+ proxy_url, -1, proxy_wide, wide_len)) {
219
+ giterr_set(GITERR_OS, "Failed to convert string to wide form");
220
+ git__free(proxy_wide);
221
+ goto on_error;
222
+ }
223
+
224
+ /* Strip any trailing forward slash on the proxy URL;
225
+ * WinHTTP doesn't like it if one is present */
226
+ if (wide_len > 1 && L'/' == proxy_wide[wide_len - 2])
227
+ proxy_wide[wide_len - 2] = L'\0';
228
+
229
+ proxy_info.dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
230
+ proxy_info.lpszProxy = proxy_wide;
231
+ proxy_info.lpszProxyBypass = NULL;
232
+
233
+ if (!WinHttpSetOption(s->request,
234
+ WINHTTP_OPTION_PROXY,
235
+ &proxy_info,
236
+ sizeof(WINHTTP_PROXY_INFO))) {
237
+ giterr_set(GITERR_OS, "Failed to set proxy");
238
+ git__free(proxy_wide);
239
+ goto on_error;
240
+ }
241
+
242
+ git__free(proxy_wide);
243
+ }
244
+
245
+ /* Strip unwanted headers (X-P2P-PeerDist, X-P2P-PeerDistEx) that WinHTTP
246
+ * adds itself. This option may not be supported by the underlying
247
+ * platform, so we do not error-check it */
248
+ WinHttpSetOption(s->request,
249
+ WINHTTP_OPTION_PEERDIST_EXTENSION_STATE,
250
+ &peerdist,
251
+ sizeof(peerdist));
252
+
253
+ /* Send Pragma: no-cache header */
254
+ if (!WinHttpAddRequestHeaders(s->request, pragma_nocache, (ULONG) -1L, WINHTTP_ADDREQ_FLAG_ADD)) {
255
+ giterr_set(GITERR_OS, "Failed to add a header to the request");
256
+ goto on_error;
257
+ }
258
+
259
+ /* Send Content-Type header -- only necessary on a POST */
260
+ if (post_verb == s->verb) {
261
+ git_buf_clear(&buf);
262
+ if (git_buf_printf(&buf, "Content-Type: application/x-git-%s-request", s->service) < 0)
263
+ goto on_error;
264
+
265
+ git__utf8_to_16(ct, MAX_CONTENT_TYPE_LEN, git_buf_cstr(&buf));
266
+
267
+ if (!WinHttpAddRequestHeaders(s->request, ct, (ULONG) -1L, WINHTTP_ADDREQ_FLAG_ADD)) {
268
+ giterr_set(GITERR_OS, "Failed to add a header to the request");
269
+ goto on_error;
270
+ }
271
+ }
272
+
273
+ /* If requested, disable certificate validation */
274
+ if (t->use_ssl) {
275
+ int flags;
276
+
277
+ if (t->owner->parent.read_flags(&t->owner->parent, &flags) < 0)
278
+ goto on_error;
279
+
280
+ if ((GIT_TRANSPORTFLAGS_NO_CHECK_CERT & flags) &&
281
+ !WinHttpSetOption(s->request, WINHTTP_OPTION_SECURITY_FLAGS,
282
+ (LPVOID)&no_check_cert_flags, sizeof(no_check_cert_flags))) {
283
+ giterr_set(GITERR_OS, "Failed to set options to ignore cert errors");
284
+ goto on_error;
285
+ }
286
+ }
287
+
288
+ /* If we have a credential on the subtransport, apply it to the request */
289
+ if (t->cred &&
290
+ t->cred->credtype == GIT_CREDTYPE_USERPASS_PLAINTEXT &&
291
+ t->auth_mechanism == GIT_WINHTTP_AUTH_BASIC &&
292
+ apply_basic_credential(s->request, t->cred) < 0)
293
+ goto on_error;
294
+
295
+ /* If no other credentials have been applied and the URL has username and
296
+ * password, use those */
297
+ if (!t->cred && t->user_from_url && t->pass_from_url) {
298
+ if (!t->url_cred &&
299
+ git_cred_userpass_plaintext_new(&t->url_cred, t->user_from_url, t->pass_from_url) < 0)
300
+ goto on_error;
301
+ if (apply_basic_credential(s->request, t->url_cred) < 0)
302
+ goto on_error;
303
+ }
304
+
305
+ /* We've done everything up to calling WinHttpSendRequest. */
306
+
307
+ error = 0;
308
+
309
+ on_error:
310
+ git__free(proxy_url);
311
+ git_buf_free(&buf);
312
+ return error;
313
+ }
314
+
315
+ static int parse_unauthorized_response(
316
+ HINTERNET request,
317
+ int *allowed_types,
318
+ int *auth_mechanism)
319
+ {
320
+ DWORD supported, first, target;
321
+
322
+ *allowed_types = 0;
323
+ *auth_mechanism = 0;
324
+
325
+ /* WinHttpQueryHeaders() must be called before WinHttpQueryAuthSchemes().
326
+ * We can assume this was already done, since we know we are unauthorized.
327
+ */
328
+ if (!WinHttpQueryAuthSchemes(request, &supported, &first, &target)) {
329
+ giterr_set(GITERR_OS, "Failed to parse supported auth schemes");
330
+ return -1;
331
+ }
332
+
333
+ if (WINHTTP_AUTH_SCHEME_BASIC & supported) {
334
+ *allowed_types |= GIT_CREDTYPE_USERPASS_PLAINTEXT;
335
+ *auth_mechanism = GIT_WINHTTP_AUTH_BASIC;
336
+ }
337
+
338
+ return 0;
339
+ }
340
+
341
+ static int write_chunk(HINTERNET request, const char *buffer, size_t len)
342
+ {
343
+ DWORD bytes_written;
344
+ git_buf buf = GIT_BUF_INIT;
345
+
346
+ /* Chunk header */
347
+ git_buf_printf(&buf, "%X\r\n", len);
348
+
349
+ if (git_buf_oom(&buf))
350
+ return -1;
351
+
352
+ if (!WinHttpWriteData(request,
353
+ git_buf_cstr(&buf), (DWORD)git_buf_len(&buf),
354
+ &bytes_written)) {
355
+ git_buf_free(&buf);
356
+ giterr_set(GITERR_OS, "Failed to write chunk header");
357
+ return -1;
358
+ }
359
+
360
+ git_buf_free(&buf);
361
+
362
+ /* Chunk body */
363
+ if (!WinHttpWriteData(request,
364
+ buffer, (DWORD)len,
365
+ &bytes_written)) {
366
+ giterr_set(GITERR_OS, "Failed to write chunk");
367
+ return -1;
368
+ }
369
+
370
+ /* Chunk footer */
371
+ if (!WinHttpWriteData(request,
372
+ "\r\n", 2,
373
+ &bytes_written)) {
374
+ giterr_set(GITERR_OS, "Failed to write chunk footer");
375
+ return -1;
376
+ }
377
+
378
+ return 0;
379
+ }
380
+
381
+ static int winhttp_stream_read(
382
+ git_smart_subtransport_stream *stream,
383
+ char *buffer,
384
+ size_t buf_size,
385
+ size_t *bytes_read)
386
+ {
387
+ winhttp_stream *s = (winhttp_stream *)stream;
388
+ winhttp_subtransport *t = OWNING_SUBTRANSPORT(s);
389
+ DWORD dw_bytes_read;
390
+ char replay_count = 0;
391
+
392
+ replay:
393
+ /* Enforce a reasonable cap on the number of replays */
394
+ if (++replay_count >= 7) {
395
+ giterr_set(GITERR_NET, "Too many redirects or authentication replays");
396
+ return -1;
397
+ }
398
+
399
+ /* Connect if necessary */
400
+ if (!s->request && winhttp_stream_connect(s) < 0)
401
+ return -1;
402
+
403
+ if (!s->received_response) {
404
+ DWORD status_code, status_code_length, content_type_length, bytes_written;
405
+ char expected_content_type_8[MAX_CONTENT_TYPE_LEN];
406
+ wchar_t expected_content_type[MAX_CONTENT_TYPE_LEN], content_type[MAX_CONTENT_TYPE_LEN];
407
+
408
+ if (!s->sent_request) {
409
+ if (!WinHttpSendRequest(s->request,
410
+ WINHTTP_NO_ADDITIONAL_HEADERS, 0,
411
+ WINHTTP_NO_REQUEST_DATA, 0,
412
+ s->post_body_len, 0)) {
413
+ giterr_set(GITERR_OS, "Failed to send request");
414
+ return -1;
415
+ }
416
+
417
+ s->sent_request = 1;
418
+ }
419
+
420
+ if (s->chunked) {
421
+ assert(s->verb == post_verb);
422
+
423
+ /* Flush, if necessary */
424
+ if (s->chunk_buffer_len > 0 &&
425
+ write_chunk(s->request, s->chunk_buffer, s->chunk_buffer_len) < 0)
426
+ return -1;
427
+
428
+ s->chunk_buffer_len = 0;
429
+
430
+ /* Write the final chunk. */
431
+ if (!WinHttpWriteData(s->request,
432
+ "0\r\n\r\n", 5,
433
+ &bytes_written)) {
434
+ giterr_set(GITERR_OS, "Failed to write final chunk");
435
+ return -1;
436
+ }
437
+ }
438
+ else if (s->post_body) {
439
+ char *buffer;
440
+ DWORD len = s->post_body_len, bytes_read;
441
+
442
+ if (INVALID_SET_FILE_POINTER == SetFilePointer(s->post_body,
443
+ 0, 0, FILE_BEGIN) &&
444
+ NO_ERROR != GetLastError()) {
445
+ giterr_set(GITERR_OS, "Failed to reset file pointer");
446
+ return -1;
447
+ }
448
+
449
+ buffer = git__malloc(CACHED_POST_BODY_BUF_SIZE);
450
+
451
+ while (len > 0) {
452
+ DWORD bytes_written;
453
+
454
+ if (!ReadFile(s->post_body, buffer,
455
+ min(CACHED_POST_BODY_BUF_SIZE, len),
456
+ &bytes_read, NULL) ||
457
+ !bytes_read) {
458
+ git__free(buffer);
459
+ giterr_set(GITERR_OS, "Failed to read from temp file");
460
+ return -1;
461
+ }
462
+
463
+ if (!WinHttpWriteData(s->request, buffer,
464
+ bytes_read, &bytes_written)) {
465
+ git__free(buffer);
466
+ giterr_set(GITERR_OS, "Failed to write data");
467
+ return -1;
468
+ }
469
+
470
+ len -= bytes_read;
471
+ assert(bytes_read == bytes_written);
472
+ }
473
+
474
+ git__free(buffer);
475
+
476
+ /* Eagerly close the temp file */
477
+ CloseHandle(s->post_body);
478
+ s->post_body = NULL;
479
+ }
480
+
481
+ if (!WinHttpReceiveResponse(s->request, 0)) {
482
+ giterr_set(GITERR_OS, "Failed to receive response");
483
+ return -1;
484
+ }
485
+
486
+ /* Verify that we got a 200 back */
487
+ status_code_length = sizeof(status_code);
488
+
489
+ if (!WinHttpQueryHeaders(s->request,
490
+ WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
491
+ WINHTTP_HEADER_NAME_BY_INDEX,
492
+ &status_code, &status_code_length,
493
+ WINHTTP_NO_HEADER_INDEX)) {
494
+ giterr_set(GITERR_OS, "Failed to retrieve status code");
495
+ return -1;
496
+ }
497
+
498
+ /* The implementation of WinHTTP prior to Windows 7 will not
499
+ * redirect to an identical URI. Some Git hosters use self-redirects
500
+ * as part of their DoS mitigation strategy. Check first to see if we
501
+ * have a redirect status code, and that we haven't already streamed
502
+ * a post body. (We can't replay a streamed POST.) */
503
+ if (!s->chunked &&
504
+ (HTTP_STATUS_MOVED == status_code ||
505
+ HTTP_STATUS_REDIRECT == status_code ||
506
+ (HTTP_STATUS_REDIRECT_METHOD == status_code &&
507
+ get_verb == s->verb) ||
508
+ HTTP_STATUS_REDIRECT_KEEP_VERB == status_code)) {
509
+
510
+ /* Check for Windows 7. This workaround is only necessary on
511
+ * Windows Vista and earlier. Windows 7 is version 6.1. */
512
+ if (!git_has_win32_version(6, 1)) {
513
+ wchar_t *location;
514
+ DWORD location_length;
515
+ int redirect_cmp;
516
+
517
+ /* OK, fetch the Location header from the redirect. */
518
+ if (WinHttpQueryHeaders(s->request,
519
+ WINHTTP_QUERY_LOCATION,
520
+ WINHTTP_HEADER_NAME_BY_INDEX,
521
+ WINHTTP_NO_OUTPUT_BUFFER,
522
+ &location_length,
523
+ WINHTTP_NO_HEADER_INDEX) ||
524
+ GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
525
+ giterr_set(GITERR_OS, "Failed to read Location header");
526
+ return -1;
527
+ }
528
+
529
+ location = git__malloc(location_length);
530
+ GITERR_CHECK_ALLOC(location);
531
+
532
+ if (!WinHttpQueryHeaders(s->request,
533
+ WINHTTP_QUERY_LOCATION,
534
+ WINHTTP_HEADER_NAME_BY_INDEX,
535
+ location,
536
+ &location_length,
537
+ WINHTTP_NO_HEADER_INDEX)) {
538
+ giterr_set(GITERR_OS, "Failed to read Location header");
539
+ git__free(location);
540
+ return -1;
541
+ }
542
+
543
+ /* Compare the Location header with the request URI */
544
+ redirect_cmp = wcscmp(location, s->request_uri);
545
+ git__free(location);
546
+
547
+ if (!redirect_cmp) {
548
+ /* Replay the request */
549
+ WinHttpCloseHandle(s->request);
550
+ s->request = NULL;
551
+ s->sent_request = 0;
552
+
553
+ goto replay;
554
+ }
555
+ }
556
+ }
557
+
558
+ /* Handle authentication failures */
559
+ if (HTTP_STATUS_DENIED == status_code &&
560
+ get_verb == s->verb && t->owner->cred_acquire_cb) {
561
+ int allowed_types;
562
+
563
+ if (parse_unauthorized_response(s->request, &allowed_types, &t->auth_mechanism) < 0)
564
+ return -1;
565
+
566
+ if (allowed_types &&
567
+ (!t->cred || 0 == (t->cred->credtype & allowed_types))) {
568
+
569
+ if (t->owner->cred_acquire_cb(&t->cred, t->owner->url, t->user_from_url, allowed_types, t->owner->cred_acquire_payload) < 0)
570
+ return -1;
571
+
572
+ assert(t->cred);
573
+
574
+ WinHttpCloseHandle(s->request);
575
+ s->request = NULL;
576
+ s->sent_request = 0;
577
+
578
+ /* Successfully acquired a credential */
579
+ goto replay;
580
+ }
581
+ }
582
+
583
+ if (HTTP_STATUS_OK != status_code) {
584
+ giterr_set(GITERR_NET, "Request failed with status code: %d", status_code);
585
+ return -1;
586
+ }
587
+
588
+ /* Verify that we got the correct content-type back */
589
+ if (post_verb == s->verb)
590
+ snprintf(expected_content_type_8, MAX_CONTENT_TYPE_LEN, "application/x-git-%s-result", s->service);
591
+ else
592
+ snprintf(expected_content_type_8, MAX_CONTENT_TYPE_LEN, "application/x-git-%s-advertisement", s->service);
593
+
594
+ git__utf8_to_16(expected_content_type, MAX_CONTENT_TYPE_LEN, expected_content_type_8);
595
+ content_type_length = sizeof(content_type);
596
+
597
+ if (!WinHttpQueryHeaders(s->request,
598
+ WINHTTP_QUERY_CONTENT_TYPE,
599
+ WINHTTP_HEADER_NAME_BY_INDEX,
600
+ &content_type, &content_type_length,
601
+ WINHTTP_NO_HEADER_INDEX)) {
602
+ giterr_set(GITERR_OS, "Failed to retrieve response content-type");
603
+ return -1;
604
+ }
605
+
606
+ if (wcscmp(expected_content_type, content_type)) {
607
+ giterr_set(GITERR_NET, "Received unexpected content-type");
608
+ return -1;
609
+ }
610
+
611
+ s->received_response = 1;
612
+ }
613
+
614
+ if (!WinHttpReadData(s->request,
615
+ (LPVOID)buffer,
616
+ (DWORD)buf_size,
617
+ &dw_bytes_read))
618
+ {
619
+ giterr_set(GITERR_OS, "Failed to read data");
620
+ return -1;
621
+ }
622
+
623
+ *bytes_read = dw_bytes_read;
624
+
625
+ return 0;
626
+ }
627
+
628
+ static int winhttp_stream_write_single(
629
+ git_smart_subtransport_stream *stream,
630
+ const char *buffer,
631
+ size_t len)
632
+ {
633
+ winhttp_stream *s = (winhttp_stream *)stream;
634
+ winhttp_subtransport *t = OWNING_SUBTRANSPORT(s);
635
+ DWORD bytes_written;
636
+
637
+ if (!s->request && winhttp_stream_connect(s) < 0)
638
+ return -1;
639
+
640
+ /* This implementation of write permits only a single call. */
641
+ if (s->sent_request) {
642
+ giterr_set(GITERR_NET, "Subtransport configured for only one write");
643
+ return -1;
644
+ }
645
+
646
+ if (!WinHttpSendRequest(s->request,
647
+ WINHTTP_NO_ADDITIONAL_HEADERS, 0,
648
+ WINHTTP_NO_REQUEST_DATA, 0,
649
+ (DWORD)len, 0)) {
650
+ giterr_set(GITERR_OS, "Failed to send request");
651
+ return -1;
652
+ }
653
+
654
+ s->sent_request = 1;
655
+
656
+ if (!WinHttpWriteData(s->request,
657
+ (LPCVOID)buffer,
658
+ (DWORD)len,
659
+ &bytes_written)) {
660
+ giterr_set(GITERR_OS, "Failed to write data");
661
+ return -1;
662
+ }
663
+
664
+ assert((DWORD)len == bytes_written);
665
+
666
+ return 0;
667
+ }
668
+
669
+ static int put_uuid_string(LPWSTR buffer, size_t buffer_len_cch)
670
+ {
671
+ UUID uuid;
672
+ RPC_STATUS status = UuidCreate(&uuid);
673
+ HRESULT result;
674
+
675
+ if (RPC_S_OK != status &&
676
+ RPC_S_UUID_LOCAL_ONLY != status &&
677
+ RPC_S_UUID_NO_ADDRESS != status) {
678
+ giterr_set(GITERR_NET, "Unable to generate name for temp file");
679
+ return -1;
680
+ }
681
+
682
+ if (buffer_len_cch < UUID_LENGTH_CCH + 1) {
683
+ giterr_set(GITERR_NET, "Buffer too small for name of temp file");
684
+ return -1;
685
+ }
686
+
687
+ result = StringCbPrintfW(
688
+ buffer, buffer_len_cch,
689
+ L"%08x%04x%04x%02x%02x%02x%02x%02x%02x%02x%02x",
690
+ uuid.Data1, uuid.Data2, uuid.Data3,
691
+ uuid.Data4[0], uuid.Data4[1], uuid.Data4[2], uuid.Data4[3],
692
+ uuid.Data4[4], uuid.Data4[5], uuid.Data4[6], uuid.Data4[7]);
693
+
694
+ if (FAILED(result)) {
695
+ giterr_set(GITERR_OS, "Unable to generate name for temp file");
696
+ return -1;
697
+ }
698
+
699
+ return 0;
700
+ }
701
+
702
+ static int get_temp_file(LPWSTR buffer, DWORD buffer_len_cch)
703
+ {
704
+ size_t len;
705
+
706
+ if (!GetTempPathW(buffer_len_cch, buffer)) {
707
+ giterr_set(GITERR_OS, "Failed to get temp path");
708
+ return -1;
709
+ }
710
+
711
+ len = wcslen(buffer);
712
+
713
+ if (buffer[len - 1] != '\\' && len < buffer_len_cch)
714
+ buffer[len++] = '\\';
715
+
716
+ if (put_uuid_string(&buffer[len], (size_t)buffer_len_cch - len) < 0)
717
+ return -1;
718
+
719
+ return 0;
720
+ }
721
+
722
+ static int winhttp_stream_write_buffered(
723
+ git_smart_subtransport_stream *stream,
724
+ const char *buffer,
725
+ size_t len)
726
+ {
727
+ winhttp_stream *s = (winhttp_stream *)stream;
728
+ winhttp_subtransport *t = OWNING_SUBTRANSPORT(s);
729
+ DWORD bytes_written;
730
+
731
+ if (!s->request && winhttp_stream_connect(s) < 0)
732
+ return -1;
733
+
734
+ /* Buffer the payload, using a temporary file so we delegate
735
+ * memory management of the data to the operating system. */
736
+ if (!s->post_body) {
737
+ wchar_t temp_path[MAX_PATH + 1];
738
+
739
+ if (get_temp_file(temp_path, MAX_PATH + 1) < 0)
740
+ return -1;
741
+
742
+ s->post_body = CreateFileW(temp_path,
743
+ GENERIC_READ | GENERIC_WRITE,
744
+ FILE_SHARE_DELETE, NULL,
745
+ CREATE_NEW,
746
+ FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE | FILE_FLAG_SEQUENTIAL_SCAN,
747
+ NULL);
748
+
749
+ if (INVALID_HANDLE_VALUE == s->post_body) {
750
+ s->post_body = NULL;
751
+ giterr_set(GITERR_OS, "Failed to create temporary file");
752
+ return -1;
753
+ }
754
+ }
755
+
756
+ if (!WriteFile(s->post_body, buffer, (DWORD)len, &bytes_written, NULL)) {
757
+ giterr_set(GITERR_OS, "Failed to write to temporary file");
758
+ return -1;
759
+ }
760
+
761
+ assert((DWORD)len == bytes_written);
762
+
763
+ s->post_body_len += bytes_written;
764
+
765
+ return 0;
766
+ }
767
+
768
+ static int winhttp_stream_write_chunked(
769
+ git_smart_subtransport_stream *stream,
770
+ const char *buffer,
771
+ size_t len)
772
+ {
773
+ winhttp_stream *s = (winhttp_stream *)stream;
774
+ winhttp_subtransport *t = OWNING_SUBTRANSPORT(s);
775
+
776
+ if (!s->request && winhttp_stream_connect(s) < 0)
777
+ return -1;
778
+
779
+ if (!s->sent_request) {
780
+ /* Send Transfer-Encoding: chunked header */
781
+ if (!WinHttpAddRequestHeaders(s->request,
782
+ transfer_encoding, (ULONG) -1L,
783
+ WINHTTP_ADDREQ_FLAG_ADD)) {
784
+ giterr_set(GITERR_OS, "Failed to add a header to the request");
785
+ return -1;
786
+ }
787
+
788
+ if (!WinHttpSendRequest(s->request,
789
+ WINHTTP_NO_ADDITIONAL_HEADERS, 0,
790
+ WINHTTP_NO_REQUEST_DATA, 0,
791
+ WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH, 0)) {
792
+ giterr_set(GITERR_OS, "Failed to send request");
793
+ return -1;
794
+ }
795
+
796
+ s->sent_request = 1;
797
+ }
798
+
799
+ if (len > CACHED_POST_BODY_BUF_SIZE) {
800
+ /* Flush, if necessary */
801
+ if (s->chunk_buffer_len > 0) {
802
+ if (write_chunk(s->request, s->chunk_buffer, s->chunk_buffer_len) < 0)
803
+ return -1;
804
+
805
+ s->chunk_buffer_len = 0;
806
+ }
807
+
808
+ /* Write chunk directly */
809
+ if (write_chunk(s->request, buffer, len) < 0)
810
+ return -1;
811
+ }
812
+ else {
813
+ /* Append as much to the buffer as we can */
814
+ int count = min(CACHED_POST_BODY_BUF_SIZE - s->chunk_buffer_len, (int)len);
815
+
816
+ if (!s->chunk_buffer)
817
+ s->chunk_buffer = git__malloc(CACHED_POST_BODY_BUF_SIZE);
818
+
819
+ memcpy(s->chunk_buffer + s->chunk_buffer_len, buffer, count);
820
+ s->chunk_buffer_len += count;
821
+ buffer += count;
822
+ len -= count;
823
+
824
+ /* Is the buffer full? If so, then flush */
825
+ if (CACHED_POST_BODY_BUF_SIZE == s->chunk_buffer_len) {
826
+ if (write_chunk(s->request, s->chunk_buffer, s->chunk_buffer_len) < 0)
827
+ return -1;
828
+
829
+ s->chunk_buffer_len = 0;
830
+
831
+ /* Is there any remaining data from the source? */
832
+ if (len > 0) {
833
+ memcpy(s->chunk_buffer, buffer, len);
834
+ s->chunk_buffer_len = (unsigned int)len;
835
+ }
836
+ }
837
+ }
838
+
839
+ return 0;
840
+ }
841
+
842
+ static void winhttp_stream_free(git_smart_subtransport_stream *stream)
843
+ {
844
+ winhttp_stream *s = (winhttp_stream *)stream;
845
+
846
+ if (s->chunk_buffer) {
847
+ git__free(s->chunk_buffer);
848
+ s->chunk_buffer = NULL;
849
+ }
850
+
851
+ if (s->post_body) {
852
+ CloseHandle(s->post_body);
853
+ s->post_body = NULL;
854
+ }
855
+
856
+ if (s->request_uri) {
857
+ git__free(s->request_uri);
858
+ s->request_uri = NULL;
859
+ }
860
+
861
+ if (s->request) {
862
+ WinHttpCloseHandle(s->request);
863
+ s->request = NULL;
864
+ }
865
+
866
+ git__free(s);
867
+ }
868
+
869
+ static int winhttp_stream_alloc(winhttp_subtransport *t, winhttp_stream **stream)
870
+ {
871
+ winhttp_stream *s;
872
+
873
+ if (!stream)
874
+ return -1;
875
+
876
+ s = git__calloc(sizeof(winhttp_stream), 1);
877
+ GITERR_CHECK_ALLOC(s);
878
+
879
+ s->parent.subtransport = &t->parent;
880
+ s->parent.read = winhttp_stream_read;
881
+ s->parent.write = winhttp_stream_write_single;
882
+ s->parent.free = winhttp_stream_free;
883
+
884
+ *stream = s;
885
+
886
+ return 0;
887
+ }
888
+
889
+ static int winhttp_connect(
890
+ winhttp_subtransport *t,
891
+ const char *url)
892
+ {
893
+ wchar_t *ua = L"git/1.0 (libgit2 " WIDEN(LIBGIT2_VERSION) L")";
894
+ wchar_t host[GIT_WIN_PATH];
895
+ int32_t port;
896
+ const char *default_port;
897
+ int ret;
898
+
899
+ if (!git__prefixcmp(url, prefix_http)) {
900
+ url = url + strlen(prefix_http);
901
+ default_port = "80";
902
+ }
903
+
904
+ if (!git__prefixcmp(url, prefix_https)) {
905
+ url += strlen(prefix_https);
906
+ default_port = "443";
907
+ t->use_ssl = 1;
908
+ }
909
+
910
+ if ((ret = gitno_extract_url_parts(&t->host, &t->port, &t->user_from_url,
911
+ &t->pass_from_url, url, default_port)) < 0)
912
+ return ret;
913
+
914
+ t->path = strchr(url, '/');
915
+
916
+ /* Prepare port */
917
+ if (git__strtol32(&port, t->port, NULL, 10) < 0)
918
+ return -1;
919
+
920
+ /* Prepare host */
921
+ git__utf8_to_16(host, GIT_WIN_PATH, t->host);
922
+
923
+ /* Establish session */
924
+ t->session = WinHttpOpen(
925
+ ua,
926
+ WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
927
+ WINHTTP_NO_PROXY_NAME,
928
+ WINHTTP_NO_PROXY_BYPASS,
929
+ 0);
930
+
931
+ if (!t->session) {
932
+ giterr_set(GITERR_OS, "Failed to init WinHTTP");
933
+ return -1;
934
+ }
935
+
936
+ /* Establish connection */
937
+ t->connection = WinHttpConnect(
938
+ t->session,
939
+ host,
940
+ port,
941
+ 0);
942
+
943
+ if (!t->connection) {
944
+ giterr_set(GITERR_OS, "Failed to connect to host");
945
+ return -1;
946
+ }
947
+
948
+ return 0;
949
+ }
950
+
951
+ static int winhttp_uploadpack_ls(
952
+ winhttp_subtransport *t,
953
+ winhttp_stream *s)
954
+ {
955
+ s->service = upload_pack_service;
956
+ s->service_url = upload_pack_ls_service_url;
957
+ s->verb = get_verb;
958
+
959
+ return 0;
960
+ }
961
+
962
+ static int winhttp_uploadpack(
963
+ winhttp_subtransport *t,
964
+ winhttp_stream *s)
965
+ {
966
+ s->service = upload_pack_service;
967
+ s->service_url = upload_pack_service_url;
968
+ s->verb = post_verb;
969
+
970
+ return 0;
971
+ }
972
+
973
+ static int winhttp_receivepack_ls(
974
+ winhttp_subtransport *t,
975
+ winhttp_stream *s)
976
+ {
977
+ s->service = receive_pack_service;
978
+ s->service_url = receive_pack_ls_service_url;
979
+ s->verb = get_verb;
980
+
981
+ return 0;
982
+ }
983
+
984
+ static int winhttp_receivepack(
985
+ winhttp_subtransport *t,
986
+ winhttp_stream *s)
987
+ {
988
+ /* WinHTTP only supports Transfer-Encoding: chunked
989
+ * on Windows Vista (NT 6.0) and higher. */
990
+ s->chunked = git_has_win32_version(6, 0);
991
+
992
+ if (s->chunked)
993
+ s->parent.write = winhttp_stream_write_chunked;
994
+ else
995
+ s->parent.write = winhttp_stream_write_buffered;
996
+
997
+ s->service = receive_pack_service;
998
+ s->service_url = receive_pack_service_url;
999
+ s->verb = post_verb;
1000
+
1001
+ return 0;
1002
+ }
1003
+
1004
+ static int winhttp_action(
1005
+ git_smart_subtransport_stream **stream,
1006
+ git_smart_subtransport *subtransport,
1007
+ const char *url,
1008
+ git_smart_service_t action)
1009
+ {
1010
+ winhttp_subtransport *t = (winhttp_subtransport *)subtransport;
1011
+ winhttp_stream *s;
1012
+ int ret = -1;
1013
+
1014
+ if (!t->connection &&
1015
+ winhttp_connect(t, url) < 0)
1016
+ return -1;
1017
+
1018
+ if (winhttp_stream_alloc(t, &s) < 0)
1019
+ return -1;
1020
+
1021
+ if (!stream)
1022
+ return -1;
1023
+
1024
+ switch (action)
1025
+ {
1026
+ case GIT_SERVICE_UPLOADPACK_LS:
1027
+ ret = winhttp_uploadpack_ls(t, s);
1028
+ break;
1029
+
1030
+ case GIT_SERVICE_UPLOADPACK:
1031
+ ret = winhttp_uploadpack(t, s);
1032
+ break;
1033
+
1034
+ case GIT_SERVICE_RECEIVEPACK_LS:
1035
+ ret = winhttp_receivepack_ls(t, s);
1036
+ break;
1037
+
1038
+ case GIT_SERVICE_RECEIVEPACK:
1039
+ ret = winhttp_receivepack(t, s);
1040
+ break;
1041
+
1042
+ default:
1043
+ assert(0);
1044
+ }
1045
+
1046
+ if (!ret)
1047
+ *stream = &s->parent;
1048
+
1049
+ return ret;
1050
+ }
1051
+
1052
+ static int winhttp_close(git_smart_subtransport *subtransport)
1053
+ {
1054
+ winhttp_subtransport *t = (winhttp_subtransport *)subtransport;
1055
+ int ret = 0;
1056
+
1057
+ if (t->host) {
1058
+ git__free(t->host);
1059
+ t->host = NULL;
1060
+ }
1061
+
1062
+ if (t->port) {
1063
+ git__free(t->port);
1064
+ t->port = NULL;
1065
+ }
1066
+
1067
+ if (t->user_from_url) {
1068
+ git__free(t->user_from_url);
1069
+ t->user_from_url = NULL;
1070
+ }
1071
+
1072
+ if (t->pass_from_url) {
1073
+ git__free(t->pass_from_url);
1074
+ t->pass_from_url = NULL;
1075
+ }
1076
+
1077
+ if (t->cred) {
1078
+ t->cred->free(t->cred);
1079
+ t->cred = NULL;
1080
+ }
1081
+
1082
+ if (t->url_cred) {
1083
+ t->url_cred->free(t->url_cred);
1084
+ t->url_cred = NULL;
1085
+ }
1086
+
1087
+ if (t->connection) {
1088
+ if (!WinHttpCloseHandle(t->connection)) {
1089
+ giterr_set(GITERR_OS, "Unable to close connection");
1090
+ ret = -1;
1091
+ }
1092
+
1093
+ t->connection = NULL;
1094
+ }
1095
+
1096
+ if (t->session) {
1097
+ if (!WinHttpCloseHandle(t->session)) {
1098
+ giterr_set(GITERR_OS, "Unable to close session");
1099
+ ret = -1;
1100
+ }
1101
+
1102
+ t->session = NULL;
1103
+ }
1104
+
1105
+ return ret;
1106
+ }
1107
+
1108
+ static void winhttp_free(git_smart_subtransport *subtransport)
1109
+ {
1110
+ winhttp_subtransport *t = (winhttp_subtransport *)subtransport;
1111
+
1112
+ winhttp_close(subtransport);
1113
+
1114
+ git__free(t);
1115
+ }
1116
+
1117
+ int git_smart_subtransport_http(git_smart_subtransport **out, git_transport *owner)
1118
+ {
1119
+ winhttp_subtransport *t;
1120
+
1121
+ if (!out)
1122
+ return -1;
1123
+
1124
+ t = git__calloc(sizeof(winhttp_subtransport), 1);
1125
+ GITERR_CHECK_ALLOC(t);
1126
+
1127
+ t->owner = (transport_smart *)owner;
1128
+ t->parent.action = winhttp_action;
1129
+ t->parent.close = winhttp_close;
1130
+ t->parent.free = winhttp_free;
1131
+
1132
+ *out = (git_smart_subtransport *) t;
1133
+ return 0;
1134
+ }
1135
+
1136
+ #endif /* GIT_WINHTTP */