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