xref: /haiku/src/kits/network/libnetservices/Geolocation.cpp (revision 4a850ca730d8282b5b924e49e09b4ba4d6db7f54)
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 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 
53 BGeolocation::BGeolocation()
54 	: fGeolocationService(kDefaultGeolocationService),
55 	fGeocodingService(kDefaultGeocodingService)
56 {
57 }
58 
59 
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
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 		uint32 networkCookie = 0;
85 		wireless_network network;
86 
87 		BNetworkDevice device(interface.Name());
88 			// TODO is that the correct way to enumerate devices?
89 
90 		while (device.GetNextNetwork(networkCookie, network) == B_OK) {
91 			if (count != 0)
92 				query += ',';
93 
94 			count++;
95 
96 			query += "\n\t\t{ \"macAddress\": \"";
97 			query += network.address.ToString().ToUpper();
98 			query += "\", \"signalStrength\": ";
99 			query << (int)network.signal_strength;
100 			query += ", \"signalToNoiseRatio\": ";
101 			query << (int)network.noise_level;
102 			query += " }";
103 		}
104 
105 	}
106 
107 	query += "\n\t]\n}\n";
108 
109 	// Check that we have enough data (we need at least 2 networks)
110 	if (count < 2)
111 		return B_DEVICE_NOT_FOUND;
112 
113 	GeolocationListener listener;
114 	BMallocIO resultBuffer;
115 
116 	// Send Request (POST JSON message)
117 	BUrlRequest* request = BUrlProtocolRoster::MakeRequest(fGeolocationService,
118 		&resultBuffer, &listener);
119 	if (request == NULL)
120 		return B_BAD_DATA;
121 
122 	BHttpRequest* http = dynamic_cast<BHttpRequest*>(request);
123 	if (http == NULL) {
124 		delete request;
125 		return B_BAD_DATA;
126 	}
127 
128 	http->SetMethod(B_HTTP_POST);
129 	BMemoryIO* io = new BMemoryIO(query.String(), query.Length());
130 	http->AdoptInputData(io, query.Length());
131 
132 	status_t result = http->Run();
133 	if (result < 0) {
134 		delete http;
135 		return result;
136 	}
137 
138 	pthread_mutex_lock(&listener.fLock);
139 	while (http->IsRunning())
140 		pthread_cond_wait(&listener.fCompletion, &listener.fLock);
141 	pthread_mutex_unlock(&listener.fLock);
142 
143 	// Parse reply
144 	const BHttpResult& reply = (const BHttpResult&)http->Result();
145 	if (reply.StatusCode() != 200) {
146 		delete http;
147 		return B_ERROR;
148 	}
149 
150 	BMessage data;
151 	result = BJson::Parse((char*)resultBuffer.Buffer(), data);
152 	delete http;
153 	if (result != B_OK) {
154 		return result;
155 	}
156 
157 	BMessage location;
158 	result = data.FindMessage("location", &location);
159 	if (result != B_OK)
160 		return result;
161 
162 	double lat, lon;
163 	result = location.FindDouble("lat", &lat);
164 	if (result != B_OK)
165 		return result;
166 	result = location.FindDouble("lng", &lon);
167 	if (result != B_OK)
168 		return result;
169 
170 	latitude = lat;
171 	longitude = lon;
172 
173 	return result;
174 }
175 
176 
177 status_t
178 BGeolocation::Country(const float latitude, const float longitude,
179 	BCountry& country)
180 {
181 	// Prepare the request URL
182 	BUrl url(fGeocodingService);
183 	BString requestString;
184 	requestString.SetToFormat("%s&lat=%f&lng=%f", url.Request().String(), latitude,
185 		longitude);
186 	url.SetPath("/countryCode");
187 	url.SetRequest(requestString);
188 
189 	GeolocationListener listener;
190 	BMallocIO resultBuffer;
191 	BUrlRequest* request = BUrlProtocolRoster::MakeRequest(url,
192 		&resultBuffer, &listener);
193 	if (request == NULL)
194 		return B_BAD_DATA;
195 
196 	BHttpRequest* http = dynamic_cast<BHttpRequest*>(request);
197 	if (http == NULL) {
198 		delete request;
199 		return B_BAD_DATA;
200 	}
201 
202 	status_t result = http->Run();
203 	if (result < 0) {
204 		delete http;
205 		return result;
206 	}
207 
208 	pthread_mutex_lock(&listener.fLock);
209 	while (http->IsRunning()) {
210 		pthread_cond_wait(&listener.fCompletion, &listener.fLock);
211 	}
212 	pthread_mutex_unlock(&listener.fLock);
213 
214 	// Parse reply
215 	const BHttpResult& reply = (const BHttpResult&)http->Result();
216 	if (reply.StatusCode() != 200) {
217 		delete http;
218 		return B_ERROR;
219 	}
220 
221 	off_t length = 0;
222 	resultBuffer.GetSize(&length);
223 	length -= 2; // Remove \r\n from response
224 	BString countryCode((char*)resultBuffer.Buffer(), (int32)length);
225 	return country.SetTo(countryCode);
226 }
227 
228 
229 #ifdef HAVE_DEFAULT_GEOLOCATION_SERVICE_KEY
230 
231 #include "DefaultGeolocationServiceKey.h"
232 
233 const char* BGeolocation::kDefaultGeolocationService
234 	= "https://location.services.mozilla.com/v1/geolocate?key="
235 		DEFAULT_GEOLOCATION_SERVICE_KEY;
236 
237 const char* BGeolocation::kDefaultGeocodingService
238 	= "https://secure.geonames.org/?username="
239 		DEFAULT_GEOCODING_SERVICE_KEY;
240 
241 #else
242 
243 const char* BGeolocation::kDefaultGeolocationService = "";
244 const char* BGeolocation::kDefaultGeocodingService = "";
245 
246 #endif
247 
248 }	// namespace Network
249 
250 }	// namespace BPrivate
251