* Copyright 2014, Haiku, Inc. All Rights Reserved.
* Copyright 2019, Adrien Destugues, pulkomandy@pulkomandy.tk
* Distributed under the terms of the MIT License.
*/
#include <Geolocation.h>
#include <HttpRequest.h>
#include <Json.h>
#include <NetworkDevice.h>
#include <NetworkInterface.h>
#include <NetworkRoster.h>
#include <String.h>
#include <UrlProtocolRoster.h>
#include <UrlRequest.h>
namespace BPrivate {
namespace Network {
class GeolocationListener: public BUrlProtocolListener
{
public:
GeolocationListener()
{
pthread_cond_init(&fCompletion, NULL);
pthread_mutex_init(&fLock, NULL);
}
virtual ~GeolocationListener() {
pthread_cond_destroy(&fCompletion);
pthread_mutex_destroy(&fLock);
}
void ConnectionOpened(BUrlRequest* caller)
{
pthread_mutex_lock(&fLock);
}
void RequestCompleted(BUrlRequest* caller, bool success) {
pthread_cond_signal(&fCompletion);
pthread_mutex_unlock(&fLock);
}
pthread_cond_t fCompletion;
pthread_mutex_t fLock;
};
BGeolocation::BGeolocation()
: fGeolocationService(kDefaultGeolocationService, true),
fGeocodingService(kDefaultGeocodingService, true)
{
}
BGeolocation::BGeolocation(const BUrl& geolocationService,
const BUrl& geocodingService)
: fGeolocationService(geolocationService),
fGeocodingService(geocodingService)
{
if (!fGeolocationService.IsValid())
fGeolocationService.SetUrlString(kDefaultGeolocationService, true);
if (!fGeocodingService.IsValid())
fGeocodingService.SetUrlString(kDefaultGeocodingService, true);
}
status_t
BGeolocation::LocateSelf(float& latitude, float& longitude)
{
BNetworkRoster& roster = BNetworkRoster::Default();
uint32 interfaceCookie = 0;
BNetworkInterface interface;
BString query("{\n\t\"wifiAccessPoints\": [");
int32 count = 0;
while (roster.GetNextInterface(&interfaceCookie, interface) == B_OK) {
BNetworkDevice device(interface.Name());
uint32 networksCount = 0;
wireless_network* networks = NULL;
device.GetNetworks(networks, networksCount);
for (uint32 i = 0; i < networksCount; i++) {
const wireless_network& network = networks[i];
if (count != 0)
query += ',';
count++;
query += "\n\t\t{ \"macAddress\": \"";
query += network.address.ToString().ToUpper();
query += "\", \"signalStrength\": ";
query << (int)network.signal_strength;
query += ", \"signalToNoiseRatio\": ";
query << (int)network.noise_level;
query += " }";
}
delete[] networks;
}
query += "\n\t]\n}\n";
if (count < 2)
return B_DEVICE_NOT_FOUND;
GeolocationListener listener;
BMallocIO resultBuffer;
BUrlRequest* request = BUrlProtocolRoster::MakeRequest(fGeolocationService,
&resultBuffer, &listener);
if (request == NULL)
return B_BAD_DATA;
BHttpRequest* http = dynamic_cast<BHttpRequest*>(request);
if (http == NULL) {
delete request;
return B_BAD_DATA;
}
http->SetMethod(B_HTTP_POST);
BMemoryIO* io = new BMemoryIO(query.String(), query.Length());
http->AdoptInputData(io, query.Length());
status_t result = http->Run();
if (result < 0) {
delete http;
return result;
}
pthread_mutex_lock(&listener.fLock);
while (http->IsRunning())
pthread_cond_wait(&listener.fCompletion, &listener.fLock);
pthread_mutex_unlock(&listener.fLock);
const BHttpResult& reply = (const BHttpResult&)http->Result();
if (reply.StatusCode() != 200) {
delete http;
return B_ERROR;
}
BMessage data;
result = BJson::Parse((char*)resultBuffer.Buffer(), data);
delete http;
if (result != B_OK) {
return result;
}
BMessage location;
result = data.FindMessage("location", &location);
if (result != B_OK)
return result;
double lat, lon;
result = location.FindDouble("lat", &lat);
if (result != B_OK)
return result;
result = location.FindDouble("lng", &lon);
if (result != B_OK)
return result;
latitude = lat;
longitude = lon;
return result;
}
status_t
BGeolocation::Country(const float latitude, const float longitude,
BCountry& country)
{
BUrl url(fGeocodingService);
BString requestString;
requestString.SetToFormat("%s&lat=%f&lng=%f", url.Request().String(), latitude,
longitude);
url.SetPath("/countryCode");
url.SetRequest(requestString);
GeolocationListener listener;
BMallocIO resultBuffer;
BUrlRequest* request = BUrlProtocolRoster::MakeRequest(url,
&resultBuffer, &listener);
if (request == NULL)
return B_BAD_DATA;
BHttpRequest* http = dynamic_cast<BHttpRequest*>(request);
if (http == NULL) {
delete request;
return B_BAD_DATA;
}
status_t result = http->Run();
if (result < 0) {
delete http;
return result;
}
pthread_mutex_lock(&listener.fLock);
while (http->IsRunning()) {
pthread_cond_wait(&listener.fCompletion, &listener.fLock);
}
pthread_mutex_unlock(&listener.fLock);
const BHttpResult& reply = (const BHttpResult&)http->Result();
if (reply.StatusCode() != 200) {
delete http;
return B_ERROR;
}
off_t length = 0;
resultBuffer.GetSize(&length);
length -= 2;
BString countryCode((char*)resultBuffer.Buffer(), (int32)length);
return country.SetTo(countryCode);
}
#ifdef HAVE_DEFAULT_GEOLOCATION_SERVICE_KEY
#include "DefaultGeolocationServiceKey.h"
const char* BGeolocation::kDefaultGeolocationService
= "https://location.services.mozilla.com/v1/geolocate?key="
DEFAULT_GEOLOCATION_SERVICE_KEY;
const char* BGeolocation::kDefaultGeocodingService
= "https://secure.geonames.org/?username="
DEFAULT_GEOCODING_SERVICE_KEY;
#else
const char* BGeolocation::kDefaultGeolocationService = "";
const char* BGeolocation::kDefaultGeocodingService = "";
#endif
}
}