1 /**
2 	...
3 	
4 	Copyright: © 2012 Matthias Dondorff
5 	License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file.
6 	Authors: Matthias Dondorff
7 */
8 module dub.internal.utils;
9 
10 import dub.internal.std.process;
11 import dub.internal.vibecompat.core.file;
12 import dub.internal.vibecompat.core.log;
13 import dub.internal.vibecompat.data.json;
14 import dub.internal.vibecompat.inet.url;
15 import dub.version_;
16 
17 // todo: cleanup imports.
18 import std.algorithm : startsWith;
19 import std.array;
20 import std.conv;
21 import std.exception;
22 import std.file;
23 import std.net.curl;
24 import std.string;
25 import std.typecons;
26 import std.zip;
27 
28 
29 Path getTempDir()
30 {
31 	auto tmp = environment.get("TEMP");
32 	if( !tmp.length ) tmp = environment.get("TMP");
33 	if( !tmp.length ){
34 		version(Posix) tmp = "/tmp/";
35 		else tmp = "./";
36 	}
37 	return Path(tmp);
38 }
39 
40 bool isEmptyDir(Path p) {
41 	foreach(DirEntry e; dirEntries(p.toNativeString(), SpanMode.shallow))
42 		return false;
43 	return true;
44 }
45 
46 Json jsonFromFile(Path file, bool silent_fail = false) {
47 	if( silent_fail && !existsFile(file) ) return Json.EmptyObject;
48 	auto f = openFile(file.toNativeString(), FileMode.Read);
49 	scope(exit) f.close();
50 	auto text = stripUTF8Bom(cast(string)f.readAll());
51 	return parseJson(text);
52 }
53 
54 Json jsonFromZip(Path zip, string filename) {
55 	auto f = openFile(zip, FileMode.Read);
56 	ubyte[] b = new ubyte[cast(size_t)f.size];
57 	f.rawRead(b);
58 	f.close();
59 	auto archive = new ZipArchive(b);
60 	auto text = stripUTF8Bom(cast(string)archive.expand(archive.directory[filename]));
61 	return parseJson(text);
62 }
63 
64 void writeJsonFile(Path path, Json json)
65 {
66 	auto f = openFile(path, FileMode.CreateTrunc);
67 	scope(exit) f.close();
68 	f.writePrettyJsonString(json);
69 }
70 
71 bool isPathFromZip(string p) {
72 	enforce(p.length > 0);
73 	return p[$-1] == '/';
74 }
75 
76 bool existsDirectory(Path path) {
77 	if( !existsFile(path) ) return false;
78 	auto fi = getFileInfo(path);
79 	return fi.isDirectory;
80 }
81 
82 void runCommands(string[] commands, string[string] env = null)
83 {
84 	foreach(cmd; commands){
85 		logDiagnostic("Running %s", cmd);
86 		Pid pid;
87 		if( env !is null ) pid = spawnShell(cmd, env);
88 		else pid = spawnShell(cmd);
89 		auto exitcode = pid.wait();
90 		enforce(exitcode == 0, "Command failed with exit code "~to!string(exitcode));
91 	}
92 }
93 
94 /**
95 	Downloads a file from the specified URL.
96 
97 	Any redirects will be followed until the actual file resource is reached or if the redirection
98 	limit of 10 is reached. Note that only HTTP(S) is currently supported.
99 */
100 void download(string url, string filename)
101 {
102 	auto conn = setupHTTPClient();
103 	logDebug("Storing %s...", url);
104 	std.net.curl.download(url, filename, conn);
105 }
106 /// ditto
107 void download(Url url, Path filename)
108 {
109 	download(url.toString(), filename.toNativeString());
110 }
111 /// ditto
112 char[] download(string url)
113 {
114 	auto conn = setupHTTPClient();
115 	logDebug("Getting %s...", url);
116 	return get(url, conn);
117 }
118 /// ditto
119 char[] download(Url url)
120 {
121 	return download(url.toString());
122 }
123 
124 private HTTP setupHTTPClient()
125 {
126 	auto conn = HTTP();
127 	static if( is(typeof(&conn.verifyPeer)) )
128 		conn.verifyPeer = false;
129 
130 	// convert version string to valid SemVer format
131 	auto verstr = dubVersion;
132 	if (verstr.startsWith("v")) verstr = verstr[1 .. $];
133 	auto idx = verstr.indexOf("-");
134 	if (idx >= 0) verstr = verstr[0 .. idx] ~ "+" ~ verstr[idx+1 .. $].split("-").join(".");
135 
136 	conn.addRequestHeader("User-Agent", "dub/"~verstr~" (std.net.curl; +https://github.com/rejectedsoftware/dub)");
137 	return conn;
138 }
139 
140 private string stripUTF8Bom(string str)
141 {
142 	if( str.length >= 3 && str[0 .. 3] == [0xEF, 0xBB, 0xBF] )
143 		return str[3 ..$];
144 	return str;
145 }