1 // Copyright (c) 2017 Matthew Brennan Jones <matthew.brennan.jones@gmail.com>
2 // Boost Software License - Version 1.0
3 // Get http://ipinfo.io info with the D programming language
4 // https://github.com/workhorsy/d-ipinfo
5 
6 /++
7 Get http://ipinfo.io with the D programming language.
8 
9 Home page:
10 $(LINK https://github.com/workhorsy/d-ipinfo)
11 
12 Version: 1.1.0
13 
14 License:
15 Boost Software License - Version 1.0
16 
17 Examples:
18 ----
19 import std.stdio : stdout, stderr;
20 import ipinfo : getIpinfo, IpinfoData;
21 
22 getIpinfo(delegate(IpinfoData data, Exception err) {
23 	if (err) {
24 		stderr.writefln("%s", err);
25 	} else {
26 		stdout.writefln("ip: %s", data.ip);
27 		stdout.writefln("latitude: %s", data.latitude);
28 		stdout.writefln("longitude: %s", data.longitude);
29 		stdout.writefln("org: %s", data.org);
30 		stdout.writefln("city: %s", data.city);
31 		stdout.writefln("region: %s", data.region);
32 		stdout.writefln("country: %s", data.country);
33 		stdout.writefln("postal: %s", data.postal);
34 	}
35 });
36 
37 /*
38 ip: 203.205.28.14
39 latitude: 37.385999999999996
40 longitude: -122.0838
41 org: AS15169 Google Inc.
42 city: Mountain View
43 region: California
44 country: US
45 postal: 94043
46 */
47 ----
48 +/
49 
50 module ipinfo;
51 
52 /++
53 Data gathered in IpinfoData:
54 ----
55 struct IpinfoData {
56 	string ip;
57 	string latitude;
58 	string longitude;
59 	string org;
60 	string city;
61 	string region;
62 	string country;
63 	string postal;
64 }
65 ----
66 +/
67 
68 struct IpinfoData {
69 	string ip;
70 	string latitude;
71 	string longitude;
72 	string org;
73 	string city;
74 	string region;
75 	string country;
76 	string postal;
77 }
78 
79 void delegate(string url, void delegate(int status, string response) cb) httpGet;
80 private void delegate(string url, void delegate(int status, string response) cb) httpGetDefault;
81 
82 static this() {
83 	httpGetDefault = delegate(string url, void delegate(int status, string response) cb) {
84 		import std.net.curl : HTTP, CurlException, get;
85 
86 		auto http = HTTP();
87 		string content = "";
88 
89 		try {
90 			content = cast(string) get(url, http);
91 		} catch (CurlException ex) {
92 		}
93 
94 		ushort status = http.statusLine().code;
95 		cb(status, content);
96 	};
97 
98 	httpGet = httpGetDefault;
99 }
100 
101 /++
102 Returns the ipinfo info using a callback.
103 
104 Params:
105  cb = The callback to fire when the ipinfo has been downloaded. The callback
106 	sends the IpinfoData data and an Exception if there was any problem.
107 +/
108 void getIpinfo(void delegate(IpinfoData data, Exception err) cb) {
109 	import std.json : JSONValue, parseJSON;
110 	import std..string : chomp, format;
111 	import std.array : split;
112 
113 	IpinfoData data;
114 	immutable string URL = "https://ipinfo.io/json";
115 
116 	// Get ipinfo for this ip address
117 	httpGet(URL, delegate(int status, string response) {
118 		if (status != 200) {
119 			auto err = new Exception("Request for \"%s\" failed with status code: %s".format(URL, status));
120 			cb(data, err);
121 			return;
122 		}
123 
124 		try {
125 			JSONValue j = parseJSON(response);
126 
127 			string[] loc = split(j["loc"].str(), ",");
128 
129 			data.ip = j["ip"].str();
130 			data.latitude = chomp(loc[0]);
131 			data.longitude = chomp(loc[1]);
132 			data.org = j["org"].str();
133 			data.city = j["city"].str();
134 			data.region = j["region"].str();
135 			data.country = j["country"].str();
136 			data.postal = j["postal"].str();
137 		} catch (Throwable) {
138 			auto err = new Exception("Failed to parse \"%s\" JSON response".format(URL));
139 			cb(data, err);
140 			return;
141 		}
142 
143 		cb(data, null);
144 	});
145 }