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