@ohos-ports/curlconverter 4.12.0-beta.0

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 (270) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +153 -0
  3. package/dist/src/Headers.d.ts +23 -0
  4. package/dist/src/Headers.js +287 -0
  5. package/dist/src/Headers.js.map +1 -0
  6. package/dist/src/Query.d.ts +8 -0
  7. package/dist/src/Query.js +136 -0
  8. package/dist/src/Query.js.map +1 -0
  9. package/dist/src/Request.d.ts +200 -0
  10. package/dist/src/Request.js +1191 -0
  11. package/dist/src/Request.js.map +1 -0
  12. package/dist/src/Warnings.d.ts +15 -0
  13. package/dist/src/Warnings.js +111 -0
  14. package/dist/src/Warnings.js.map +1 -0
  15. package/dist/src/cli.d.ts +2 -0
  16. package/dist/src/cli.js +333 -0
  17. package/dist/src/cli.js.map +1 -0
  18. package/dist/src/curl/auth.d.ts +12 -0
  19. package/dist/src/curl/auth.js +40 -0
  20. package/dist/src/curl/auth.js.map +1 -0
  21. package/dist/src/curl/form.d.ts +28 -0
  22. package/dist/src/curl/form.js +218 -0
  23. package/dist/src/curl/form.js.map +1 -0
  24. package/dist/src/curl/opts.d.ts +2052 -0
  25. package/dist/src/curl/opts.js +1000 -0
  26. package/dist/src/curl/opts.js.map +1 -0
  27. package/dist/src/curl/url.d.ts +14 -0
  28. package/dist/src/curl/url.js +218 -0
  29. package/dist/src/curl/url.js.map +1 -0
  30. package/dist/src/generators/ansible.d.ts +5 -0
  31. package/dist/src/generators/ansible.js +282 -0
  32. package/dist/src/generators/ansible.js.map +1 -0
  33. package/dist/src/generators/c.d.ts +12 -0
  34. package/dist/src/generators/c.js +1230 -0
  35. package/dist/src/generators/c.js.map +1 -0
  36. package/dist/src/generators/cfml.d.ts +5 -0
  37. package/dist/src/generators/cfml.js +134 -0
  38. package/dist/src/generators/cfml.js.map +1 -0
  39. package/dist/src/generators/clojure.d.ts +7 -0
  40. package/dist/src/generators/clojure.js +436 -0
  41. package/dist/src/generators/clojure.js.map +1 -0
  42. package/dist/src/generators/csharp.d.ts +8 -0
  43. package/dist/src/generators/csharp.js +356 -0
  44. package/dist/src/generators/csharp.js.map +1 -0
  45. package/dist/src/generators/dart.d.ts +5 -0
  46. package/dist/src/generators/dart.js +273 -0
  47. package/dist/src/generators/dart.js.map +1 -0
  48. package/dist/src/generators/elixir.d.ts +8 -0
  49. package/dist/src/generators/elixir.js +296 -0
  50. package/dist/src/generators/elixir.js.map +1 -0
  51. package/dist/src/generators/go.d.ts +5 -0
  52. package/dist/src/generators/go.js +228 -0
  53. package/dist/src/generators/go.js.map +1 -0
  54. package/dist/src/generators/har.d.ts +7 -0
  55. package/dist/src/generators/har.js +115 -0
  56. package/dist/src/generators/har.js.map +1 -0
  57. package/dist/src/generators/http.d.ts +7 -0
  58. package/dist/src/generators/http.js +165 -0
  59. package/dist/src/generators/http.js.map +1 -0
  60. package/dist/src/generators/httpie.d.ts +5 -0
  61. package/dist/src/generators/httpie.js +446 -0
  62. package/dist/src/generators/httpie.js.map +1 -0
  63. package/dist/src/generators/java/httpurlconnection.d.ts +5 -0
  64. package/dist/src/generators/java/httpurlconnection.js +123 -0
  65. package/dist/src/generators/java/httpurlconnection.js.map +1 -0
  66. package/dist/src/generators/java/java.d.ts +8 -0
  67. package/dist/src/generators/java/java.js +247 -0
  68. package/dist/src/generators/java/java.js.map +1 -0
  69. package/dist/src/generators/java/jsoup.d.ts +5 -0
  70. package/dist/src/generators/java/jsoup.js +152 -0
  71. package/dist/src/generators/java/jsoup.js.map +1 -0
  72. package/dist/src/generators/java/okhttp.d.ts +5 -0
  73. package/dist/src/generators/java/okhttp.js +232 -0
  74. package/dist/src/generators/java/okhttp.js.map +1 -0
  75. package/dist/src/generators/javascript/axios.d.ts +5 -0
  76. package/dist/src/generators/javascript/axios.js +332 -0
  77. package/dist/src/generators/javascript/axios.js.map +1 -0
  78. package/dist/src/generators/javascript/got.d.ts +5 -0
  79. package/dist/src/generators/javascript/got.js +277 -0
  80. package/dist/src/generators/javascript/got.js.map +1 -0
  81. package/dist/src/generators/javascript/http.d.ts +8 -0
  82. package/dist/src/generators/javascript/http.js +183 -0
  83. package/dist/src/generators/javascript/http.js.map +1 -0
  84. package/dist/src/generators/javascript/javascript.d.ts +37 -0
  85. package/dist/src/generators/javascript/javascript.js +676 -0
  86. package/dist/src/generators/javascript/javascript.js.map +1 -0
  87. package/dist/src/generators/javascript/jquery.d.ts +15 -0
  88. package/dist/src/generators/javascript/jquery.js +297 -0
  89. package/dist/src/generators/javascript/jquery.js.map +1 -0
  90. package/dist/src/generators/javascript/ky.d.ts +7 -0
  91. package/dist/src/generators/javascript/ky.js +245 -0
  92. package/dist/src/generators/javascript/ky.js.map +1 -0
  93. package/dist/src/generators/javascript/request.d.ts +5 -0
  94. package/dist/src/generators/javascript/request.js +113 -0
  95. package/dist/src/generators/javascript/request.js.map +1 -0
  96. package/dist/src/generators/javascript/superagent.d.ts +10 -0
  97. package/dist/src/generators/javascript/superagent.js +301 -0
  98. package/dist/src/generators/javascript/superagent.js.map +1 -0
  99. package/dist/src/generators/javascript/xhr.d.ts +8 -0
  100. package/dist/src/generators/javascript/xhr.js +147 -0
  101. package/dist/src/generators/javascript/xhr.js.map +1 -0
  102. package/dist/src/generators/json.d.ts +45 -0
  103. package/dist/src/generators/json.js +197 -0
  104. package/dist/src/generators/json.js.map +1 -0
  105. package/dist/src/generators/julia.d.ts +8 -0
  106. package/dist/src/generators/julia.js +365 -0
  107. package/dist/src/generators/julia.js.map +1 -0
  108. package/dist/src/generators/kotlin.d.ts +8 -0
  109. package/dist/src/generators/kotlin.js +325 -0
  110. package/dist/src/generators/kotlin.js.map +1 -0
  111. package/dist/src/generators/lua.d.ts +5 -0
  112. package/dist/src/generators/lua.js +192 -0
  113. package/dist/src/generators/lua.js.map +1 -0
  114. package/dist/src/generators/matlab/common.d.ts +16 -0
  115. package/dist/src/generators/matlab/common.js +238 -0
  116. package/dist/src/generators/matlab/common.js.map +1 -0
  117. package/dist/src/generators/matlab/httpinterface.d.ts +2 -0
  118. package/dist/src/generators/matlab/httpinterface.js +211 -0
  119. package/dist/src/generators/matlab/httpinterface.js.map +1 -0
  120. package/dist/src/generators/matlab/matlab.d.ts +5 -0
  121. package/dist/src/generators/matlab/matlab.js +29 -0
  122. package/dist/src/generators/matlab/matlab.js.map +1 -0
  123. package/dist/src/generators/matlab/webservices.d.ts +2 -0
  124. package/dist/src/generators/matlab/webservices.js +191 -0
  125. package/dist/src/generators/matlab/webservices.js.map +1 -0
  126. package/dist/src/generators/objectivec.d.ts +8 -0
  127. package/dist/src/generators/objectivec.js +269 -0
  128. package/dist/src/generators/objectivec.js.map +1 -0
  129. package/dist/src/generators/ocaml.d.ts +8 -0
  130. package/dist/src/generators/ocaml.js +150 -0
  131. package/dist/src/generators/ocaml.js.map +1 -0
  132. package/dist/src/generators/perl.d.ts +9 -0
  133. package/dist/src/generators/perl.js +209 -0
  134. package/dist/src/generators/perl.js.map +1 -0
  135. package/dist/src/generators/php/guzzle.d.ts +5 -0
  136. package/dist/src/generators/php/guzzle.js +328 -0
  137. package/dist/src/generators/php/guzzle.js.map +1 -0
  138. package/dist/src/generators/php/php.d.ts +8 -0
  139. package/dist/src/generators/php/php.js +217 -0
  140. package/dist/src/generators/php/php.js.map +1 -0
  141. package/dist/src/generators/php/requests.d.ts +5 -0
  142. package/dist/src/generators/php/requests.js +89 -0
  143. package/dist/src/generators/php/requests.js.map +1 -0
  144. package/dist/src/generators/powershell.d.ts +11 -0
  145. package/dist/src/generators/powershell.js +377 -0
  146. package/dist/src/generators/powershell.js.map +1 -0
  147. package/dist/src/generators/python/http.d.ts +5 -0
  148. package/dist/src/generators/python/http.js +128 -0
  149. package/dist/src/generators/python/http.js.map +1 -0
  150. package/dist/src/generators/python/python.d.ts +22 -0
  151. package/dist/src/generators/python/python.js +1693 -0
  152. package/dist/src/generators/python/python.js.map +1 -0
  153. package/dist/src/generators/r/httr.d.ts +10 -0
  154. package/dist/src/generators/r/httr.js +298 -0
  155. package/dist/src/generators/r/httr.js.map +1 -0
  156. package/dist/src/generators/r/httr2.d.ts +5 -0
  157. package/dist/src/generators/r/httr2.js +214 -0
  158. package/dist/src/generators/r/httr2.js.map +1 -0
  159. package/dist/src/generators/ruby/httparty.d.ts +6 -0
  160. package/dist/src/generators/ruby/httparty.js +308 -0
  161. package/dist/src/generators/ruby/httparty.js.map +1 -0
  162. package/dist/src/generators/ruby/ruby.d.ts +14 -0
  163. package/dist/src/generators/ruby/ruby.js +515 -0
  164. package/dist/src/generators/ruby/ruby.js.map +1 -0
  165. package/dist/src/generators/rust.d.ts +8 -0
  166. package/dist/src/generators/rust.js +177 -0
  167. package/dist/src/generators/rust.js.map +1 -0
  168. package/dist/src/generators/swift.d.ts +8 -0
  169. package/dist/src/generators/swift.js +219 -0
  170. package/dist/src/generators/swift.js.map +1 -0
  171. package/dist/src/generators/wget.d.ts +8 -0
  172. package/dist/src/generators/wget.js +472 -0
  173. package/dist/src/generators/wget.js.map +1 -0
  174. package/dist/src/index.d.ts +53 -0
  175. package/dist/src/index.js +54 -0
  176. package/dist/src/index.js.map +1 -0
  177. package/dist/src/parse.d.ts +13 -0
  178. package/dist/src/parse.js +43 -0
  179. package/dist/src/parse.js.map +1 -0
  180. package/dist/src/shell/Parser.d.ts +4 -0
  181. package/dist/src/shell/Parser.js +6 -0
  182. package/dist/src/shell/Parser.js.map +1 -0
  183. package/dist/src/shell/Word.d.ts +48 -0
  184. package/dist/src/shell/Word.js +486 -0
  185. package/dist/src/shell/Word.js.map +1 -0
  186. package/dist/src/shell/tokenizer.d.ts +3 -0
  187. package/dist/src/shell/tokenizer.js +610 -0
  188. package/dist/src/shell/tokenizer.js.map +1 -0
  189. package/dist/src/shell/webParser.d.ts +4 -0
  190. package/dist/src/shell/webParser.js +15 -0
  191. package/dist/src/shell/webParser.js.map +1 -0
  192. package/dist/src/utils.d.ts +6 -0
  193. package/dist/src/utils.js +21 -0
  194. package/dist/src/utils.js.map +1 -0
  195. package/dist/tools/compare-requests.d.ts +2 -0
  196. package/dist/tools/compare-requests.js +448 -0
  197. package/dist/tools/compare-requests.js.map +1 -0
  198. package/dist/tools/gen-test.d.ts +2 -0
  199. package/dist/tools/gen-test.js +114 -0
  200. package/dist/tools/gen-test.js.map +1 -0
  201. package/dist/tree-sitter-bash.wasm +0 -0
  202. package/index.d.ts +3 -0
  203. package/package.json +97 -0
  204. package/src/Headers.ts +334 -0
  205. package/src/Query.ts +147 -0
  206. package/src/Request.ts +1649 -0
  207. package/src/Warnings.ts +154 -0
  208. package/src/cli.ts +561 -0
  209. package/src/curl/auth.ts +45 -0
  210. package/src/curl/form.ts +297 -0
  211. package/src/curl/opts.ts +1417 -0
  212. package/src/curl/url.ts +241 -0
  213. package/src/generators/ansible.ts +364 -0
  214. package/src/generators/c.ts +1371 -0
  215. package/src/generators/cfml.ts +155 -0
  216. package/src/generators/clojure.ts +521 -0
  217. package/src/generators/csharp.ts +383 -0
  218. package/src/generators/dart.ts +293 -0
  219. package/src/generators/elixir.ts +349 -0
  220. package/src/generators/go.ts +269 -0
  221. package/src/generators/har.ts +147 -0
  222. package/src/generators/http.ts +194 -0
  223. package/src/generators/httpie.ts +506 -0
  224. package/src/generators/java/httpurlconnection.ts +151 -0
  225. package/src/generators/java/java.ts +280 -0
  226. package/src/generators/java/jsoup.ts +172 -0
  227. package/src/generators/java/okhttp.ts +257 -0
  228. package/src/generators/javascript/axios.ts +395 -0
  229. package/src/generators/javascript/got.ts +362 -0
  230. package/src/generators/javascript/http.ts +232 -0
  231. package/src/generators/javascript/javascript.ts +825 -0
  232. package/src/generators/javascript/jquery.ts +360 -0
  233. package/src/generators/javascript/ky.ts +315 -0
  234. package/src/generators/javascript/request.ts +152 -0
  235. package/src/generators/javascript/superagent.ts +373 -0
  236. package/src/generators/javascript/xhr.ts +192 -0
  237. package/src/generators/json.ts +293 -0
  238. package/src/generators/julia.ts +398 -0
  239. package/src/generators/kotlin.ts +355 -0
  240. package/src/generators/lua.ts +209 -0
  241. package/src/generators/matlab/common.ts +281 -0
  242. package/src/generators/matlab/httpinterface.ts +284 -0
  243. package/src/generators/matlab/matlab.ts +39 -0
  244. package/src/generators/matlab/webservices.ts +263 -0
  245. package/src/generators/objectivec.ts +297 -0
  246. package/src/generators/ocaml.ts +168 -0
  247. package/src/generators/perl.ts +237 -0
  248. package/src/generators/php/guzzle.ts +368 -0
  249. package/src/generators/php/php.ts +239 -0
  250. package/src/generators/php/requests.ts +103 -0
  251. package/src/generators/powershell.ts +430 -0
  252. package/src/generators/python/http.ts +167 -0
  253. package/src/generators/python/python.ts +1934 -0
  254. package/src/generators/r/httr.ts +328 -0
  255. package/src/generators/r/httr2.ts +268 -0
  256. package/src/generators/ruby/httparty.ts +351 -0
  257. package/src/generators/ruby/ruby.ts +564 -0
  258. package/src/generators/rust.ts +243 -0
  259. package/src/generators/swift.ts +235 -0
  260. package/src/generators/wget.ts +533 -0
  261. package/src/index.ts +89 -0
  262. package/src/parse.ts +72 -0
  263. package/src/shell/Parser.ts +8 -0
  264. package/src/shell/Word.ts +531 -0
  265. package/src/shell/tokenizer.ts +736 -0
  266. package/src/shell/webParser.ts +18 -0
  267. package/src/utils.ts +25 -0
  268. package/tools/compare-requests.ts +571 -0
  269. package/tools/extract_curl_args.py +511 -0
  270. package/tools/gen-test.ts +129 -0
@@ -0,0 +1,241 @@
1
+ import { Word, eq } from "../shell/Word.js";
2
+ import { warnf } from "../Warnings.js";
3
+ import type { GlobalConfig, OperationConfig } from "./opts.js";
4
+
5
+ // https://github.com/curl/curl/blob/curl-7_87_0/lib/urlapi.c#L60
6
+ // https://github.com/curl/curl/blob/curl-7_87_0/lib/urldata.h#L1295
7
+ export interface Curl_URL {
8
+ scheme: Word;
9
+
10
+ auth?: Word;
11
+ user?: Word;
12
+ password?: Word;
13
+
14
+ // options: string /* IMAP only? */;
15
+ host: Word;
16
+ // zoneid: string /* for numerical IPv6 addresses */;
17
+ port: Word;
18
+ path: Word;
19
+ query: Word;
20
+ fragment: Word;
21
+ // portnum: number /* the numerical version */;
22
+ }
23
+
24
+ // https://github.com/curl/curl/blob/curl-7_88_1/src/tool_urlglob.c#L327
25
+ const MAX_IP6LEN = 128;
26
+ function isIpv6(glob: string): boolean {
27
+ if (glob.length > MAX_IP6LEN) {
28
+ return false;
29
+ }
30
+ // TODO: curl tries to parse the glob as a hostname.
31
+ return !glob.includes("-");
32
+ }
33
+
34
+ function warnAboutGlobs(global_: GlobalConfig, url: string) {
35
+ // Find any glob expressions in the URL and underline them
36
+ let prev = "";
37
+ for (let i = 0; i < url.length; i++) {
38
+ const cur = url[i];
39
+ if (cur === "[" && prev !== "\\") {
40
+ let j = i + 1;
41
+ while (j < url.length && url[j] !== "]") {
42
+ j++;
43
+ }
44
+ if (j < url.length && url[j] === "]") {
45
+ const glob = url.slice(i, j + 1);
46
+ // could be ipv6 address
47
+ if (!isIpv6(glob)) {
48
+ warnf(global_, [
49
+ "glob-in-url",
50
+ `globs in the URL are not supported:\n` +
51
+ `${url}\n` +
52
+ " ".repeat(i) +
53
+ "^".repeat(glob.length),
54
+ ]);
55
+ }
56
+ prev = "";
57
+ } else {
58
+ // No closing bracket
59
+ warnf(global_, [
60
+ "unbalanced-glob",
61
+ "bracket doesn't have a closing bracket:\n" +
62
+ `${url}\n` +
63
+ `${" ".repeat(i)}^`,
64
+ ]);
65
+ return; // malformed URL, stop looking for globs
66
+ }
67
+ } else if (cur === "{" && prev !== "\\") {
68
+ let j = i + 1;
69
+ while (j < url.length && url[j] !== "}") {
70
+ j++;
71
+ }
72
+ if (j < url.length && url[j] === "}") {
73
+ const glob = url.slice(i, j + 1);
74
+ warnf(global_, [
75
+ "glob-in-url",
76
+ `globs in the URL are not supported:\n` +
77
+ `${url}\n` +
78
+ " ".repeat(i) +
79
+ "^".repeat(glob.length),
80
+ ]);
81
+ prev = "";
82
+ } else {
83
+ // No closing bracket
84
+ warnf(global_, [
85
+ "unbalanced-glob",
86
+ "bracket doesn't have a closing bracket:\n" +
87
+ `${url}\n` +
88
+ `${" ".repeat(i)}^`,
89
+ ]);
90
+ return; // malformed URL, stop looking for globs
91
+ }
92
+ }
93
+
94
+ prev = cur;
95
+ }
96
+ }
97
+
98
+ export function parseurl(
99
+ global_: GlobalConfig,
100
+ config: OperationConfig,
101
+ url: Word,
102
+ ): Curl_URL {
103
+ // This is curl's parseurl()
104
+ // https://github.com/curl/curl/blob/curl-7_85_0/lib/urlapi.c#L1144
105
+ // Except we want to accept all URLs.
106
+ // curl further validates URLs in curl_url_get()
107
+ // https://github.com/curl/curl/blob/curl-7_86_0/lib/urlapi.c#L1374
108
+ const u: Curl_URL = {
109
+ scheme: new Word(),
110
+ host: new Word(),
111
+ port: new Word(),
112
+ path: new Word(), // with leading '/'
113
+ query: new Word(), // with leading '?'
114
+ fragment: new Word(), // with leading '#'
115
+ };
116
+
117
+ // Remove url glob escapes
118
+ // https://github.com/curl/curl/blob/curl-7_87_0/src/tool_urlglob.c#L395-L398
119
+ if (!config.globoff) {
120
+ if (url.isString()) {
121
+ warnAboutGlobs(global_, url.toString());
122
+ }
123
+ url = url.replace(/\\([[\]{}])/g, "$1");
124
+ }
125
+
126
+ // Prepend "http"/"https" if the scheme is missing.
127
+ // RFC 3986 3.1 says
128
+ // scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
129
+ // but curl will accept a digit/plus/minus/dot in the first character
130
+ // curl will also accept a url with one / like http:/localhost
131
+ // https://github.com/curl/curl/blob/curl-7_85_0/lib/urlapi.c#L960
132
+ let schemeMatch = null;
133
+ if (url.tokens.length && typeof url.tokens[0] === "string") {
134
+ schemeMatch = url.tokens[0].match(/^([a-zA-Z0-9+-.]*):\/\/*/);
135
+ }
136
+ if (schemeMatch) {
137
+ const [schemeAndSlashes, scheme] = schemeMatch;
138
+ u.scheme = new Word(scheme.toLowerCase());
139
+ url = url.slice(schemeAndSlashes.length);
140
+ } else {
141
+ // curl defaults to https://
142
+ // we don't because most libraries won't downgrade to http
143
+ // if you ask for https, unlike curl.
144
+ // TODO: handle file:// scheme
145
+ u.scheme = config["proto-default"] ?? new Word("http");
146
+ }
147
+ if (!eq(u.scheme, "http") && !eq(u.scheme, "https")) {
148
+ warnf(global_, ["bad-scheme", `Protocol "${u.scheme}" not supported`]);
149
+ }
150
+
151
+ // https://github.com/curl/curl/blob/curl-7_85_0/lib/urlapi.c#L992
152
+ const hostMatch = url.indexOfFirstChar("/?#");
153
+ if (hostMatch !== -1) {
154
+ u.host = url.slice(0, hostMatch);
155
+ // TODO: u.path might end up empty if indexOfFirstChar found ?#
156
+ u.path = url.slice(hostMatch); // keep leading '/' in .path
157
+ // https://github.com/curl/curl/blob/curl-7_85_0/lib/urlapi.c#L1024
158
+ const fragmentIndex = u.path.indexOf("#");
159
+ const queryIndex = u.path.indexOf("?");
160
+ if (fragmentIndex !== -1) {
161
+ u.fragment = u.path.slice(fragmentIndex);
162
+ if (queryIndex !== -1 && queryIndex < fragmentIndex) {
163
+ u.query = u.path.slice(queryIndex, fragmentIndex);
164
+ u.path = u.path.slice(0, queryIndex);
165
+ } else {
166
+ u.path = u.path.slice(0, fragmentIndex);
167
+ }
168
+ } else if (queryIndex !== -1) {
169
+ u.query = u.path.slice(queryIndex);
170
+ u.path = u.path.slice(0, queryIndex);
171
+ }
172
+ } else {
173
+ u.host = url;
174
+ }
175
+
176
+ // parse username:password@hostname
177
+ // https://github.com/curl/curl/blob/curl-7_85_0/lib/urlapi.c#L1083
178
+ // https://github.com/curl/curl/blob/curl-7_85_0/lib/urlapi.c#L460
179
+ // https://github.com/curl/curl/blob/curl-7_85_0/lib/url.c#L2827
180
+ const authMatch = u.host.indexOf("@");
181
+ if (authMatch !== -1) {
182
+ const auth = u.host.slice(0, authMatch);
183
+ u.host = u.host.slice(authMatch + 1); // throw away '@'
184
+ if (!config["disallow-username-in-url"]) {
185
+ u.auth = auth;
186
+ if (auth.includes(":")) {
187
+ [u.user, u.password] = auth.split(":", 2);
188
+ } else {
189
+ u.user = auth;
190
+ u.password = new Word(); // if there's no ':', curl will append it
191
+ }
192
+ } else {
193
+ // Curl will exit if this is the case, but we just remove it from the URL
194
+ warnf(global_, [
195
+ "login-denied",
196
+ `Found auth in URL but --disallow-username-in-url was passed: ${auth.toString()}`,
197
+ ]);
198
+ }
199
+ }
200
+
201
+ // TODO: need to extract port first
202
+ // hostname_check()
203
+ // https://github.com/curl/curl/blob/curl-7_86_0/lib/urlapi.c#L572
204
+ // if (!u.host) {
205
+ // warnf(global_, [
206
+ // "no-host",
207
+ // "Found empty host in URL: " + JSON.stringify(url),
208
+ // ]);
209
+ // } else if (u.host.startsWith("[")) {
210
+ // if (!u.host.endsWith("]")) {
211
+ // warnf(global_, [
212
+ // "bad-host",
213
+ // "Found invalid IPv6 address in URL: " + JSON.stringify(url),
214
+ // ]);
215
+ // } else {
216
+ // const firstWeirdCharacter = u.host.match(/[^0123456789abcdefABCDEF:.]/);
217
+ // // %zone_id
218
+ // if (firstWeirdCharacter && firstWeirdCharacter[0] !== "%") {
219
+ // warnf(global_, [
220
+ // "bad-host",
221
+ // "Found invalid IPv6 address in URL: " + JSON.stringify(url),
222
+ // ]);
223
+ // }
224
+ // }
225
+ // } else {
226
+ // const firstInvalidCharacter = u.host.match(
227
+ // /[\r\n\t/:#?!@{}[\]\\$'"^`*<>=;,]/
228
+ // );
229
+ // if (firstInvalidCharacter) {
230
+ // warnf(global_, [
231
+ // "bad-host",
232
+ // "Found invalid character " +
233
+ // JSON.stringify(firstInvalidCharacter[0]) +
234
+ // " in URL: " +
235
+ // JSON.stringify(url),
236
+ // ]);
237
+ // }
238
+ // }
239
+
240
+ return u;
241
+ }
@@ -0,0 +1,364 @@
1
+ import { Word, eq } from "../shell/Word.js";
2
+ import { parse, getFirst, COMMON_SUPPORTED_ARGS } from "../parse.js";
3
+ import type { Request, Warnings } from "../parse.js";
4
+ import { parseQueryString, type QueryList, type QueryDict } from "../Query.js";
5
+
6
+ import yaml from "yamljs";
7
+
8
+ export const supportedArgs = new Set([
9
+ ...COMMON_SUPPORTED_ARGS,
10
+ "compressed",
11
+ "no-compressed", // only explicitly disabling compression has an effect
12
+
13
+ "netrc",
14
+ "netrc-optional",
15
+ "no-netrc", // only explicitly disabling netrc has an effect
16
+ "no-netrc-optional",
17
+
18
+ "cacert",
19
+ "cert",
20
+ "key",
21
+ "ciphers",
22
+ "insecure",
23
+ "no-insecure",
24
+
25
+ "anyauth",
26
+ "no-anyauth",
27
+ "digest",
28
+ "no-digest",
29
+ "aws-sigv4",
30
+ "negotiate",
31
+ "no-negotiate",
32
+ // "delegation", // GSS/kerberos
33
+ // Not suported, just more specific warning
34
+ "ntlm",
35
+ "no-ntlm",
36
+ "ntlm-wb",
37
+ "no-ntlm-wb",
38
+
39
+ "unix-socket",
40
+ "abstract-unix-socket",
41
+
42
+ "max-time",
43
+
44
+ "location",
45
+ "no-location",
46
+ "location-trusted",
47
+ "no-location-trusted",
48
+
49
+ "upload-file",
50
+ "output",
51
+
52
+ "form",
53
+ "form-string",
54
+ ]);
55
+
56
+ // https://docs.ansible.com/ansible/latest/collections/ansible/builtin/uri_module.html
57
+ type BodyFormat =
58
+ | "form-urlencoded"
59
+ | "json"
60
+ | "raw" // default
61
+ | "form-multipart";
62
+ type AnsibleForm = {
63
+ [key: string]: {
64
+ content?: string;
65
+ filename?: string;
66
+ mime_type?: string;
67
+ };
68
+ };
69
+ type Body = QueryList | QueryDict | object | string | AnsibleForm;
70
+ type AnsibleURI = {
71
+ url: string;
72
+ method?: string;
73
+ headers?: { [key: string]: string };
74
+ body?: Body;
75
+ body_format?: BodyFormat;
76
+ src?: string; // shouldn't have body if has src
77
+ dest?: string;
78
+
79
+ url_username?: string;
80
+ url_password?: string;
81
+ force_basic_auth?: boolean;
82
+ use_gssapi?: boolean;
83
+ use_netrc?: boolean; // true
84
+
85
+ // String won't work, it's in case we can't parse it as a number
86
+ timeout?: number | string; // 30
87
+
88
+ decompress?: boolean;
89
+ follow_redirects?:
90
+ | "all"
91
+ | "no"
92
+ | "none"
93
+ | "safe" // default
94
+ | "urllib2"
95
+ | "yes";
96
+
97
+ ca_path?: string;
98
+ ciphers?: string;
99
+ client_cert?: string;
100
+ client_key?: string;
101
+ validate_certs?: boolean;
102
+
103
+ unix_socket?: string;
104
+ };
105
+
106
+ function getDataString(
107
+ request: Request,
108
+ warnings: Warnings,
109
+ ): [Body, BodyFormat] | [string, "src"] | undefined {
110
+ if (!request.data || !request.data.isString()) {
111
+ return;
112
+ }
113
+ const data = request.data.toString();
114
+
115
+ const contentType = request.headers.getContentType();
116
+ // TODO: delete Content-Type header when it's what Ansible will send anyway
117
+ // const exactContentType = request.headers.get("Content-Type");
118
+ if (contentType === "application/json") {
119
+ try {
120
+ const dataAsJson = JSON.parse(data);
121
+ // TODO: if doesn't roundtrip, add commented out raw string
122
+ // TODO: we actually want to know how it's serialized by Ansible
123
+ // const roundtrips = JSON.stringify(dataAsJson) === dataStr;
124
+ return [dataAsJson, "json"];
125
+ } catch {}
126
+ } else if (contentType === "application/x-www-form-urlencoded") {
127
+ if (
128
+ request.dataArray &&
129
+ request.dataArray.length === 1 &&
130
+ !(request.dataArray[0] instanceof Word) &&
131
+ !request.dataArray[0].name
132
+ ) {
133
+ const filetype = request.dataArray[0].filetype;
134
+ const filename = request.dataArray[0].filename.toString();
135
+ // TODO: and newlines have to be stripped for others
136
+ if (filetype === "urlencode") {
137
+ // TODO: or the other ones don't need to be?
138
+ warnings.push([
139
+ "urlencoded-file",
140
+ "the file needs to be urlencoded: " + JSON.stringify(filename),
141
+ ]);
142
+ }
143
+ return [filename, "src"];
144
+ }
145
+ const [queryList, queryDict] = parseQueryString(request.data);
146
+ if (queryDict) {
147
+ return [
148
+ Object.fromEntries(
149
+ queryDict.map((q) => [
150
+ q[0].toString(),
151
+ Array.isArray(q[1])
152
+ ? q[1].map((qv) => qv.toString())
153
+ : q[1].toString(),
154
+ ]),
155
+ ),
156
+ "form-urlencoded",
157
+ ];
158
+ }
159
+ if (queryList) {
160
+ return [
161
+ queryList.map((q) => [q[0].toString(), q[1].toString()]),
162
+ "form-urlencoded",
163
+ ];
164
+ }
165
+ }
166
+ return;
167
+ }
168
+
169
+ export function _toAnsible(
170
+ requests: Request[],
171
+ warnings: Warnings = [],
172
+ ): string {
173
+ // Only supported if there's one file and nothing else
174
+ const request = getFirst(requests, warnings, { dataReadsFile: true });
175
+ if (
176
+ request.dataReadsFile &&
177
+ request.dataArray &&
178
+ (request.dataArray.length > 1 ||
179
+ (!(request.dataArray[0] instanceof Word) && request.dataArray[0].name))
180
+ ) {
181
+ warnings.push([
182
+ "unsafe-data",
183
+ "the generated body is wrong, " +
184
+ JSON.stringify("@" + request.dataReadsFile) +
185
+ " means read the file " +
186
+ JSON.stringify(request.dataReadsFile),
187
+ ]);
188
+ }
189
+
190
+ const r: AnsibleURI = {
191
+ url: request.urls[0].url.toString(),
192
+ method: request.urls[0].method.toString(),
193
+ };
194
+ if (request.multipartUploads) {
195
+ const form: AnsibleForm = {};
196
+ for (const m of request.multipartUploads) {
197
+ // TODO: can't have duplicate keys
198
+ const name = m.name.toString();
199
+ form[name] = {};
200
+ if ("content" in m) {
201
+ form[name].content = m.content.toString();
202
+ } else {
203
+ // TODO: get basename
204
+ form[name].filename = m.contentFile.toString();
205
+ if (m.filename && !eq(m.filename, m.contentFile)) {
206
+ warnings.push([
207
+ "multipart-fake-filename",
208
+ // TODO: check if this is true, the only source is it's not in the example
209
+ // https://docs.ansible.com/ansible/latest/collections/ansible/builtin/uri_module.html#examples
210
+ "Ansible doesn't allow reading from a file and changing the sent filename: " +
211
+ JSON.stringify(m.contentFile.toString()),
212
+ ]);
213
+ }
214
+ }
215
+ if ("contentType" in m && m.contentType) {
216
+ form[name].mime_type = m.contentType.toString();
217
+ }
218
+ }
219
+ r.body = form;
220
+ r.body_format = "form-multipart";
221
+ } else if (request.data) {
222
+ const d = getDataString(request, warnings);
223
+ if (d) {
224
+ const [body, format] = d;
225
+ if (format === "src") {
226
+ r.src = body;
227
+ } else {
228
+ r.body = body;
229
+ if (format !== "raw") {
230
+ r.body_format = format;
231
+ }
232
+ }
233
+ } else {
234
+ r.body = request.data.toString();
235
+ }
236
+ } else if (request.urls[0].uploadFile) {
237
+ r.src = request.urls[0].uploadFile.toString();
238
+ }
239
+ if (request.urls[0].output) {
240
+ r.dest = request.urls[0].output.toString();
241
+ }
242
+
243
+ if (request.headers.length) {
244
+ r.headers = {};
245
+ for (const h of request.headers) {
246
+ const [k, v] = h;
247
+ if (v !== null) {
248
+ r.headers[k.toString()] = v.toString();
249
+ }
250
+ }
251
+ }
252
+ if (request.urls[0].auth) {
253
+ const [username, password] = request.urls[0].auth;
254
+ if (username.toBool()) {
255
+ r.url_username = username.toString();
256
+ }
257
+ if (password.toBool()) {
258
+ r.url_password = password.toString();
259
+ }
260
+ }
261
+ // No idea if this stuff is correct
262
+ switch (request.authType) {
263
+ case "basic":
264
+ // TODO: if --basic explicitly passed, set this?
265
+ // r.force_basic_auth = true;
266
+ break;
267
+ case "negotiate":
268
+ r.use_gssapi = true;
269
+ break;
270
+ case "ntlm":
271
+ case "ntlm-wb":
272
+ // https://docs.ansible.com/ansible/latest/collections/ansible/builtin/uri_module.html#parameter-use_gssapi
273
+ warnings.push(["ntlm", "Ansible doesn't support NTLM authentication."]);
274
+ break;
275
+ }
276
+
277
+ if (request.cacert) {
278
+ r.ca_path = request.cacert.toString();
279
+ }
280
+ if (request.cert) {
281
+ // TODO: might have password
282
+ const [cert, password] = request.cert;
283
+ r.client_cert = cert.toString();
284
+ if (password) {
285
+ warnings.push([
286
+ "cert-password",
287
+ "Ansible does not support client certificate passwords: " +
288
+ JSON.stringify(password.toString()),
289
+ ]);
290
+ }
291
+ }
292
+ if (request.key) {
293
+ r.client_key = request.key.toString();
294
+ }
295
+ if (request.ciphers) {
296
+ r.ciphers = request.ciphers.toString();
297
+ }
298
+ if (request.insecure) {
299
+ r.validate_certs = false;
300
+ }
301
+ if (request.netrc === "ignored") {
302
+ r.use_netrc = false;
303
+ }
304
+
305
+ if (request.compressed === false) {
306
+ r.decompress = false;
307
+ }
308
+ // curl defaults to not following redirects, Ansible defaults to following
309
+ // safe redirects. Don't change that default unless it's explicitly set.
310
+ if (request.followRedirects === false) {
311
+ r.follow_redirects = "none";
312
+ } else if (request.followRedirects === true) {
313
+ // curl will follow redirects to a different host but not send credentials
314
+ // Ansible will only follow redirects for GET or HEAD to the same host
315
+ r.follow_redirects = request.followRedirectsTrusted ? "all" : "safe";
316
+ }
317
+
318
+ if (request.timeout) {
319
+ const timeoutStr = request.timeout.toString();
320
+ const timeoutInt = parseInt(timeoutStr); // TODO: warn if wasn't int
321
+
322
+ if (isNaN(timeoutInt)) {
323
+ r.timeout = timeoutStr;
324
+ warnings.push([
325
+ "ansible-timeout-not-int",
326
+ "couldn't parse timeout as an integer: " + JSON.stringify(timeoutStr),
327
+ ]);
328
+ } else {
329
+ r.timeout = timeoutInt;
330
+ }
331
+
332
+ warnings.push([
333
+ "ansible-timeout",
334
+ "Ansible's timeout is socket-level but curl's is for the entire request.",
335
+ ]);
336
+ }
337
+
338
+ if (request.unixSocket) {
339
+ r.unix_socket = request.unixSocket.toString();
340
+ }
341
+
342
+ return yaml.stringify(
343
+ [
344
+ {
345
+ name: request.urls[0].urlWithoutQueryList.toString(),
346
+ uri: r,
347
+ register: "result",
348
+ },
349
+ ],
350
+ 100,
351
+ 2,
352
+ );
353
+ }
354
+ export function toAnsibleWarn(
355
+ curlCommand: string | string[],
356
+ warnings: Warnings = [],
357
+ ): [string, Warnings] {
358
+ const requests = parse(curlCommand, supportedArgs, warnings);
359
+ const ansible = _toAnsible(requests, warnings);
360
+ return [ansible, warnings];
361
+ }
362
+ export function toAnsible(curlCommand: string | string[]): string {
363
+ return toAnsibleWarn(curlCommand)[0];
364
+ }