Files
SubMusic/source/AmpacheAPI.mc
memen45 fd0a6d4926 Rewrite of sync engine
Added PlaylistStore classes
Fixed SongStore class
Added setting for AmpacheAPI (commented)
Many bug fixes
Fixed version
2020-06-21 21:25:08 +02:00

137 lines
3.6 KiB
MonkeyC

using Toybox.Time;
using Toybox.Cryptography;
using Toybox.Communications;
class AmpacheAPI {
private var d_url;
private var d_usr;
private var d_client = WatchUi.loadResource(Rez.Strings.AppName);
private var d_hash; // password hash, required for every handshake
private var d_auth;
private var d_auth_expire;
function initialize(settings) {
d_url = settings.get("api_url") + "/server/json.server.php";
d_usr = settings.get("api_usr");
var hasher = new Cryptography.Hash({:algorithm => Cryptography.HASH_SHA256});
// hash the password
hasher.update(settings.get("api_key"));
d_hash = hasher.digest();
// check if auth is expired, it may be usable!
// set expire time to 0
d_auth_expire = new Time.Moment(0);
}
function handshake() {
var hasher = new Cryptography.Hash({:algorithm => Cryptography.HASH_SHA256});
// get the time
var timestamp = Time.now().value();
// construct the auth
hasher.update(timestamp);
hasher.update(d_hash);
d_auth = hasher.digest();
var params = {
"action" => "handshake",
"user" => d_user,
"timestamp" => timestamp,
"auth" => d_auth,
};
Communications.makeWebRequest(d_url, params, {}, self.method(:onHandshake));
}
function onHandshake(responseCode, data) {
System.println("AmpacheAPI: onHandshake with responseCode " + responseCode + " payload " + data);
// check if request was successful
if ((responseCode != 200)
|| (data == null)
|| (data["error"] != null)) {
d_fallback.invoke(responseCode, data);
return;
}
// store the auth key for future communication
d_auth = data["auth"];
var expire = parseISODate(data["session_expire"]);
if (expire != null) {
d_auth_expire = expire;
}
}
// returns true if the current session is not expired
function session() {
var now = new Time.Moment(Time.now().value());
return now.lessThan(d_auth_expire);
}
// converts rfc3339 formatted timestamp to Time::Moment (null on error)
function parseISODate(date) {
// assert(date instanceOf String)
if (date == null) {
return null;
}
// 0123456789012345678901234
// 2011-10-17T13:00:00-07:00
// 2011-10-17T16:30:55.000Z
// 2011-10-17T16:30:55Z
if (date.length() < 20) {
return null;
}
var moment = Gregorian.moment({
:year => date.substring( 0, 4).toNumber(),
:month => date.substring( 5, 7).toNumber(),
:day => date.substring( 8, 10).toNumber(),
:hour => date.substring(11, 13).toNumber(),
:minute => date.substring(14, 16).toNumber(),
:second => date.substring(17, 19).toNumber(),
});
var suffix = date.substring(19, date.length());
// skip over to time zone
var tz = 0;
if (suffix.substring(tz, tz + 1).equals(".")) {
while (tz < suffix.length()) {
var first = suffix.substring(tz, tz + 1);
if ("-+Z".find(first) != null) {
break;
}
tz++;
}
}
if (tz >= suffix.length()) {
// no timezone given
return null;
}
var tzOffset = 0;
if (!suffix.substring(tz, tz + 1).equals("Z")) {
// +HH:MM
if (suffix.length() - tz < 6) {
return null;
}
tzOffset = suffix.substring(tz + 1, tz + 3).toNumber() * Gregorian.SECONDS_PER_HOUR;
tzOffset += suffix.substring(tz + 4, tz + 6).toNumber() * Gregorian.SECONDS_PER_MINUTE;
var sign = suffix.substring(tz, tz + 1);
if (sign.equals("+")) {
tzOffset = -tzOffset;
} else if (sign.equals("-") && tzOffset == 0) {
// -00:00 denotes unknown timezone
return null;
}
}
return moment.add(new Time.Duration(tzOffset));
}
}