xref: /haiku/src/kits/network/libnetservices/Geolocation.cpp (revision acdafb571df692fab7ad89836e39590dd8d415e2)
1 /*
2  * Copyright 2014, Haiku, Inc. All Rights Reserved.
3  * Copyright 2019, Adrien Destugues, pulkomandy@pulkomandy.tk
4  * Distributed under the terms of the MIT License.
5  */
6 
7 
8 #include <Geolocation.h>
9 
10 #include <HttpRequest.h>
11 #include <Json.h>
12 #include <NetworkDevice.h>
13 #include <NetworkInterface.h>
14 #include <NetworkRoster.h>
15 #include <String.h>
16 #include <UrlProtocolRoster.h>
17 #include <UrlRequest.h>
18 
19 
20 namespace BPrivate {
21 
22 namespace Network {
23 
24 class GeolocationListener: public BUrlProtocolListener
25 {
26 	public:
27 		GeolocationListener()
28 		{
29 			pthread_cond_init(&fCompletion, NULL);
30 			pthread_mutex_init(&fLock, NULL);
31 		}
32 
33 		virtual	~GeolocationListener() {
34 			pthread_cond_destroy(&fCompletion);
35 			pthread_mutex_destroy(&fLock);
36 		}
37 
38 		void ConnectionOpened(BUrlRequest* caller)
39 		{
40 			pthread_mutex_lock(&fLock);
41 		}
42 
43 		void DataReceived(BUrlRequest*, const char* data, off_t position,
44 					ssize_t size) {
45 			fResult.WriteAt(position, data, size);
46 		}
47 
48 		void RequestCompleted(BUrlRequest* caller, bool success) {
49 			pthread_cond_signal(&fCompletion);
50 			pthread_mutex_unlock(&fLock);
51 		}
52 
53 		BMallocIO fResult;
54 		pthread_cond_t fCompletion;
55 		pthread_mutex_t fLock;
56 };
57 
58 
59 BGeolocation::BGeolocation()
60 	: fGeolocationService(kDefaultGeolocationService),
61 	fGeocodingService(kDefaultGeocodingService)
62 {
63 }
64 
65 
66 BGeolocation::BGeolocation(const BUrl& geolocationService,
67 	const BUrl& geocodingService)
68 	: fGeolocationService(geolocationService),
69 	fGeocodingService(geocodingService)
70 {
71 	if (!fGeolocationService.IsValid())
72 		fGeolocationService.SetUrlString(kDefaultGeolocationService);
73 	if (!fGeocodingService.IsValid())
74 		fGeocodingService.SetUrlString(kDefaultGeocodingService);
75 }
76 
77 
78 status_t
79 BGeolocation::LocateSelf(float& latitude, float& longitude)
80 {
81 	// Enumerate wifi network and build JSON message
82 	BNetworkRoster& roster = BNetworkRoster::Default();
83 	uint32 interfaceCookie = 0;
84 	BNetworkInterface interface;
85 
86 	BString query("{\n\t\"wifiAccessPoints\": [");
87 	int32 count = 0;
88 
89 	while (roster.GetNextInterface(&interfaceCookie, interface) == B_OK) {
90 		uint32 networkCookie = 0;
91 		wireless_network network;
92 
93 		BNetworkDevice device(interface.Name());
94 			// TODO is that the correct way to enumerate devices?
95 
96 		while (device.GetNextNetwork(networkCookie, network) == B_OK) {
97 			if (count != 0)
98 				query += ',';
99 
100 			count++;
101 
102 			query += "\n\t\t{ \"macAddress\": \"";
103 			query += network.address.ToString().ToUpper();
104 			query += "\", \"signalStrength\": ";
105 			query << (int)network.signal_strength;
106 			query += ", \"signalToNoiseRatio\": ";
107 			query << (int)network.noise_level;
108 			query += " }";
109 		}
110 
111 	}
112 
113 	query += "\n\t]\n}\n";
114 
115 	// Check that we have enough data (we need at least 2 networks)
116 	if (count < 2)
117 		return B_DEVICE_NOT_FOUND;
118 
119 	GeolocationListener listener;
120 
121 	// Send Request (POST JSON message)
122 	BUrlRequest* request = BUrlProtocolRoster::MakeRequest(fGeolocationService,
123 		&listener);
124 	if (request == NULL)
125 		return B_BAD_DATA;
126 
127 	BHttpRequest* http = dynamic_cast<BHttpRequest*>(request);
128 	if (http == NULL) {
129 		delete request;
130 		return B_BAD_DATA;
131 	}
132 
133 	http->SetMethod(B_HTTP_POST);
134 	BMemoryIO* io = new BMemoryIO(query.String(), query.Length());
135 	http->AdoptInputData(io, query.Length());
136 
137 	status_t result = http->Run();
138 	if (result < 0) {
139 		delete http;
140 		return result;
141 	}
142 
143 	pthread_mutex_lock(&listener.fLock);
144 	while (http->IsRunning())
145 		pthread_cond_wait(&listener.fCompletion, &listener.fLock);
146 	pthread_mutex_unlock(&listener.fLock);
147 
148 	// Parse reply
149 	const BHttpResult& reply = (const BHttpResult&)http->Result();
150 	if (reply.StatusCode() != 200) {
151 		delete http;
152 		return B_ERROR;
153 	}
154 
155 	BMessage data;
156 	result = BJson::Parse((char*)listener.fResult.Buffer(), data);
157 	delete http;
158 	if (result != B_OK) {
159 		return result;
160 	}
161 
162 	BMessage location;
163 	result = data.FindMessage("location", &location);
164 	if (result != B_OK)
165 		return result;
166 
167 	double lat, lon;
168 	result = location.FindDouble("lat", &lat);
169 	if (result != B_OK)
170 		return result;
171 	result = location.FindDouble("lng", &lon);
172 	if (result != B_OK)
173 		return result;
174 
175 	latitude = lat;
176 	longitude = lon;
177 
178 	return result;
179 }
180 
181 
182 status_t
183 BGeolocation::Country(const float latitude, const float longitude,
184 	BCountry& country)
185 {
186 	// Prepare the request URL
187 	BUrl url(fGeocodingService);
188 	BString requestString;
189 	requestString.SetToFormat("%s&lat=%f&lng=%f", url.Request().String(), latitude,
190 		longitude);
191 	url.SetPath("/countryCode");
192 	url.SetRequest(requestString);
193 
194 	GeolocationListener listener;
195 	BUrlRequest* request = BUrlProtocolRoster::MakeRequest(url,
196 		&listener);
197 	if (request == NULL)
198 		return B_BAD_DATA;
199 
200 	BHttpRequest* http = dynamic_cast<BHttpRequest*>(request);
201 	if (http == NULL) {
202 		delete request;
203 		return B_BAD_DATA;
204 	}
205 
206 	status_t result = http->Run();
207 	if (result < 0) {
208 		delete http;
209 		return result;
210 	}
211 
212 	pthread_mutex_lock(&listener.fLock);
213 	while (http->IsRunning()) {
214 		pthread_cond_wait(&listener.fCompletion, &listener.fLock);
215 	}
216 	pthread_mutex_unlock(&listener.fLock);
217 
218 	// Parse reply
219 	const BHttpResult& reply = (const BHttpResult&)http->Result();
220 	if (reply.StatusCode() != 200) {
221 		delete http;
222 		return B_ERROR;
223 	}
224 
225 	off_t length = 0;
226 	listener.fResult.GetSize(&length);
227 	length -= 2; // Remove \r\n from response
228 	BString countryCode((char*)listener.fResult.Buffer(), (int32)length);
229 	return country.SetTo(countryCode);
230 }
231 
232 
233 #ifdef HAVE_DEFAULT_GEOLOCATION_SERVICE_KEY
234 
235 #include "DefaultGeolocationServiceKey.h"
236 
237 const char* BGeolocation::kDefaultGeolocationService
238 	= "https://location.services.mozilla.com/v1/geolocate?key="
239 		DEFAULT_GEOLOCATION_SERVICE_KEY;
240 
241 const char* BGeolocation::kDefaultGeocodingService
242 	= "https://secure.geonames.org/?username="
243 		DEFAULT_GEOCODING_SERVICE_KEY;
244 
245 #else
246 
247 const char* BGeolocation::kDefaultGeolocationService = "";
248 const char* BGeolocation::kDefaultGeocodingService = "";
249 
250 #endif
251 
252 }	// namespace Network
253 
254 }	// namespace BPrivate
255