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