1 module libyggdrasil;
2 
3 import std.stdio;
4 
5 import std.json;
6 import std.socket;
7 import std.string;
8 
9 private string[] getKeys()
10 {
11 	string url = "http://[21e:e795:8e82:a9e2:ff48:952d:55f2:f0bb]/static/current";
12 	string[] keys;
13 	
14 	import std.net.curl;
15 	string k = cast(string)get(url);
16 
17 	//writeln(k);
18 
19 	JSONValue json = parseJSON(k);
20 	json = json["yggnodes"];
21 
22 	keys = json.object().keys;
23 //	writeln(keys);
24 
25 	return keys;
26 }
27 
28 void main()
29 {
30 
31 
32 	
33 	
34 	Address testNode = parseAddress("201:6c56:f9d5:b7a5:8f42:b1ab:9e0e:5169", 9090);
35 	YggdrasilPeer yggNode = new YggdrasilPeer(testNode);
36 
37 	string[] keys = ["a1b0169eae0b6fb808c60fe82f29855a01e173b3a16bb286cfcfc0ed45a28afb",
38 					"b563daa08870d769e7871f581afb9ee339b8a10c2baa45f334beb1d74b0700d7"];
39 
40 	keys = getKeys();
41 	YggdrasilNode[] nodes;
42 	foreach(string k; keys)
43 	{
44 		nodes ~= yggNode.fetchNode(k);
45 	}
46 
47 	foreach(YggdrasilNode node; nodes)
48 	{
49 		writeln(node.getNodeInfo());
50 		writeln("Peers: "~to!(string)(node.getPeers()));
51 		
52 		YggdrasilRequest req = new YggdrasilRequest(RequestType.GETDHT, node.getKey());
53 		writeln(sillyWillyRequest(yggNode, req).toPrettyString());
54 		req = new YggdrasilRequest(RequestType.GETDHT, node.getKey());
55 		writeln(sillyWillyRequest(yggNode, req).toPrettyString());
56 		req = new YggdrasilRequest(RequestType.GETPEERS, node.getKey());
57 		writeln(sillyWillyRequest(yggNode, req).toPrettyString());
58 		req = new YggdrasilRequest(RequestType.GETSELF, node.getKey());
59 		writeln(sillyWillyRequest(yggNode, req).toPrettyString());
60 		
61 	}
62 
63 }
64 
65 import std.conv;
66 
67 public class NodeInfo
68 {
69 	private JSONValue nodeInfoJSON;
70 	private JSONValue operatorBlock;
71 	private string nodeName = "<no name>";
72 	private string groupName = "<no name>";
73 	private string country = "<no name>";
74 	private string key;
75 	/* TODO: Standardise */
76 	/**
77 	* Name
78 	* Owner
79 	* Contact
80 	* Group
81 	*/
82 	this(JSONValue nodeInfoJSON)
83 	{
84 		/* We only do one query (so it will be first) */
85 		writeln(nodeInfoJSON);
86 		if(nodeInfoJSON.type == JSONType.null_)
87 		{
88 			return;
89 		}
90 		key = nodeInfoJSON.object().keys[0];
91 		this.nodeInfoJSON = nodeInfoJSON[key];
92 
93 		
94 		parse();
95 		
96 	}
97 
98 	public string getName()
99 	{
100 		return nodeName;
101 	}
102 
103 	public string getGroupName()
104 	{
105 		return groupName;
106 	}
107 
108 	public string getCountry()
109 	{
110 		return country;
111 	}
112 
113 	public string getAddress()
114 	{
115 		return "unavailable";
116 	}
117 
118 	public JSONValue getOperatorBlock()
119 	{
120 		return operatorBlock;
121 	}
122 
123 	public JSONValue getFullJSON()
124 	{
125 		return nodeInfoJSON;
126 	}
127 
128 	private void parse()
129 	{
130 		foreach(string item; nodeInfoJSON.object().keys)
131 		{
132 			if(cmp(item, "name") == 0)
133 			{
134 				nodeName = nodeInfoJSON["name"].str();
135 			}
136 			else if(cmp(item, "operator") == 0)
137 			{
138 				operatorBlock = nodeInfoJSON["operator"];
139 			}
140 			else if(cmp(item, "group") == 0)
141 			{
142 				groupName = nodeInfoJSON["group"].str();
143 			}
144 			else if(cmp(item, "location") == 0)
145 			{
146 				country = nodeInfoJSON["location"].str();
147 			}
148 		}
149 	
150 	}
151 
152 	public override string toString()
153 	{
154 		return key~" (Name: "~nodeName~", Group: "~groupName~", Operator: "~to!(string)(operatorBlock)~")";
155 	}
156 }
157 
158 /**
159 * YggdrasilNode
160 *
161 * Given a key
162 */
163 public class YggdrasilNode
164 {
165 	private YggdrasilPeer peer;
166 	
167 	private string key;
168 	private JSONValue selfInfo;
169 	private JSONValue nodeInfo;
170 	
171 	
172 	this(YggdrasilPeer peer, string key)
173 	{
174 		this.peer = peer;
175 		this.key = key;
176 	}
177 
178 	public NodeInfo getNodeInfo()
179 	{
180 		YggdrasilRequest req = new YggdrasilRequest(RequestType.NODEINFO, key);
181 		return new NodeInfo(sillyWillyRequest(peer, req));
182 	}
183 
184 	public YggdrasilNode[] getPeers()
185 	{
186 		YggdrasilNode[] peers;
187 
188 		YggdrasilRequest req = new YggdrasilRequest(RequestType.GETPEERS, key);
189 		JSONValue resp = sillyWillyRequest(peer, req);
190 
191 		if(resp.type != JSONType.null_)
192 		{
193 			foreach(JSONValue ckey; resp[resp.object().keys[0]]["keys"].array())
194 			{
195 				string ckeyStr = ckey.str();
196 				peers ~= new YggdrasilNode(peer, ckeyStr);
197 			}
198 		}
199 
200 		return peers;
201 	}
202 
203 	public string getKey()
204 	{
205 		return key;
206 	}
207 
208 	public override string toString()
209 	{
210 		/* TODO: Fetch getNodeInfo, if possible, else leave key */
211 				
212 				return getNodeInfo().toString();
213 	}
214 }
215 
216 /**
217 * YggdrasilNode
218 *
219 * This represents a peer of which we can
220 * connect to its control socket using TCP
221 */
222 public class YggdrasilPeer
223 {
224 	private Address yggdrasilAddress;
225 
226 	this(Address yggdrasilAddress)
227 	{
228 		this.yggdrasilAddress = yggdrasilAddress;
229 
230 		/* Fetch data over socket and set */
231 		
232 	}
233 
234 	public Address getAddress()
235 	{
236 		return yggdrasilAddress;
237 	}
238 
239 	private void initData()
240 	{
241 		/* TODO: Add exception throwing here */
242 
243 			
244 	}
245 
246 	public YggdrasilNode fetchNode(string key)
247 	{
248 		return new YggdrasilNode(this, key);		
249 	}
250 }
251 
252 public enum RequestType
253 {
254 	NODEINFO, GETDHT, GETPEERS, GETSELF
255 }
256 
257 public final class YggdrasilRequest
258 {
259 	private RequestType requestType;
260 	private string key;
261 
262 	this(RequestType requestType, string key)
263 	{
264 		this.requestType = requestType;
265 		this.key = key;
266 	} 
267 
268 
269 	public JSONValue generateJSON()
270 	{
271 		JSONValue requestBlock;
272 
273 		/* Set the key of the node to request from */
274 		requestBlock["key"] = key;
275 
276 		if(requestType == RequestType.NODEINFO)
277 		{
278 			requestBlock["request"] = "getnodeinfo";
279 		}
280 		else if(requestType == RequestType.GETSELF)
281 		{
282 			requestBlock["request"] = "debug_remotegetself";
283 		}
284 		else if(requestType == RequestType.GETPEERS)
285 		{
286 			requestBlock["request"] = "debug_remotegetpeers";
287 		}
288 		else if(requestType == RequestType.GETDHT)
289 		{
290 			requestBlock["request"] = "debug_remotegetdht";
291 		}
292 		
293 
294 
295 		return requestBlock;
296 	}
297 }
298 
299 /* TODO: Fix read here */
300 public JSONValue sillyWillyRequest(YggdrasilPeer peer, YggdrasilRequest request)
301 {
302 	Socket controlSocket = new Socket(AddressFamily.INET6, SocketType.STREAM, ProtocolType.TCP);
303 	controlSocket.connect(peer.getAddress());
304 
305 	JSONValue requestBlock = request.generateJSON();
306 	controlSocket.send(cast(byte[])toJSON(requestBlock));
307 
308 
309 	// TODO: Add loop reader here
310 	byte[] buffer;
311 	buffer.length = 100000;
312 	controlSocket.receive(buffer);
313 
314 
315 	writeln(parseJSON(cast(string)buffer));
316 	JSONValue responseBlock;
317 
318 	if(cmp((parseJSON(cast(string)buffer)["status"]).str(), "success") == 0)
319 	{
320 		responseBlock = parseJSON(cast(string)buffer)["response"];
321 	}
322 
323 	
324 	
325 	return responseBlock;
326 }