diff --git a/server/api/flukso/priv/dispatch.conf b/server/api/flukso/priv/dispatch.conf index c56046c..6901858 100644 --- a/server/api/flukso/priv/dispatch.conf +++ b/server/api/flukso/priv/dispatch.conf @@ -1 +1,2 @@ {["sensor", sensor], flukso_sensor_xyz, []}. +{["device", device], flukso_device_xyz, []}. diff --git a/server/api/flukso/src/flukso.app b/server/api/flukso/src/flukso.app index 0fe0969..554f3fc 100644 --- a/server/api/flukso/src/flukso.app +++ b/server/api/flukso/src/flukso.app @@ -6,7 +6,8 @@ flukso_app, flukso_sup, flukso_deps, - flukso_sensor_xyz + flukso_sensor_xyz, + flukso_device_xyz ]}, {registered, []}, {mod, {flukso_app, []}}, diff --git a/server/api/flukso/src/flukso.erl b/server/api/flukso/src/flukso.erl index deb9853..4c1ff2c 100644 --- a/server/api/flukso/src/flukso.erl +++ b/server/api/flukso/src/flukso.erl @@ -32,10 +32,13 @@ ensure_started(App) -> mysql_prepare() -> mysql:prepare(watchdog, <<"INSERT INTO watchdog (uid, type, message, variables, severity, location, hostname, timestamp) VALUES (?, ?, ?, ?, ?, ?, ?, ?)">>), mysql:prepare(permissions, <<"SELECT permissions FROM logger_tokens WHERE meter = ? AND token = ?">>), - mysql:prepare(device_key, <<"SELECT sha FROM (logger_devices ld INNER JOIN logger_meters lm ON ld.device = lm.device) WHERE lm.meter = ?">>), + mysql:prepare(sensor_key, <<"SELECT sha FROM (logger_devices ld INNER JOIN logger_meters lm ON ld.device = lm.device) WHERE lm.meter = ?">>), mysql:prepare(sensor_props, <<"SELECT uid, device, night FROM logger_meters WHERE meter = ?">>), mysql:prepare(sensor_update, <<"UPDATE logger_meters SET access = ?, night = ?, value = ? WHERE meter = ?">>), - mysql:prepare(timezone, <<"SELECT timezone FROM users WHERE uid = ?">>). + mysql:prepare(timezone, <<"SELECT timezone FROM users WHERE uid = ?">>), + mysql:prepare(device_key, <<"SELECT sha FROM logger_devices WHERE device = ?">>), + mysql:prepare(device_props, <<"SELECT sha, upgrade, resets FROM logger_devices WHERE device = ?">>), + mysql:prepare(device_update, <<"UPDATE logger_devices SET access = ?, version = ?, upgrade = ?, resets = ?, uptime = ?, memtotal = ?, memfree = ?, memcached = ?, membuffers = ? WHERE device = ?">>). %% @spec start_link() -> {ok,Pid::pid()} %% @doc Starts the app for inclusion in a supervisor tree diff --git a/server/api/flukso/src/flukso.hrl b/server/api/flukso/src/flukso.hrl index 2439ba1..52c688d 100644 --- a/server/api/flukso/src/flukso.hrl +++ b/server/api/flukso/src/flukso.hrl @@ -57,6 +57,9 @@ check_version(_, _) -> check_sensor(Sensor) -> check_hex(Sensor, 32). +check_device(Device) -> + check_hex(Device, 32). + check_token(undefined, undefined) -> {false, false}; check_token(Token, undefined) -> diff --git a/server/api/flukso/src/flukso_device_xyz.erl b/server/api/flukso/src/flukso_device_xyz.erl new file mode 100644 index 0000000..bd73b94 --- /dev/null +++ b/server/api/flukso/src/flukso_device_xyz.erl @@ -0,0 +1,122 @@ +%% @author Bart Van Der Meerssche +%% @copyright (C) 2011 Bart Van Der Meerssche +%%% +%%% This program is free software: you can redistribute it and/or modify +%%% it under the terms of the GNU General Public License as published by +%%% the Free Software Foundation, either version 3 of the License, or +%%% (at your option) any later version. +%%% +%%% This program is distributed in the hope that it will be useful, +%%% but WITHOUT ANY WARRANTY; without even the implied warranty of +%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +%%% GNU General Public License for more details. +%%% +%%% You should have received a copy of the GNU General Public License +%%% along with this program. If not, see . +%%% +%% @doc Flukso API: /device/xyz resource specification + +-module(flukso_device_xyz). +-author('Bart Van Der Meerssche '). + +-export([init/1, + allowed_methods/2, + malformed_request/2, + is_authorized/2, + process_post/2]). + +-include_lib("webmachine/include/webmachine.hrl"). +-include("flukso.hrl"). + +init([]) -> + {ok, undefined}. + +% debugging +%init(Config) -> +% {{trace, "/tmp"}, Config}. + +allowed_methods(ReqData, State) -> + {['POST'], ReqData, State}. + +malformed_request(ReqData, State) -> + case wrq:method(ReqData) of + 'POST' -> malformed_POST(ReqData, State) + end. + +malformed_POST(ReqData, _State) -> + {_Version, ValidVersion} = check_version(wrq:get_req_header("X-Version", ReqData)), + {Device, ValidDevice} = check_device(wrq:path_info(device, ReqData)), + {Digest, ValidDigest} = check_digest(wrq:get_req_header("X-Digest", ReqData)), + + State = #state{device = Device, + digest = Digest}, + + {case {ValidVersion, ValidDevice, ValidDigest} of + {true, true, true} -> false; + _ -> true + end, + ReqData, State}. + +is_authorized(ReqData, State) -> + case wrq:method(ReqData) of + 'POST' -> is_auth_POST(ReqData, State) + end. + +is_auth_POST(ReqData, #state{device = Device, digest = ClientDigest} = State) -> + {data, Result} = mysql:execute(pool, device_key, [Device]), + + case mysql:get_result_rows(Result) of + [[Key]] -> + Data = wrq:req_body(ReqData), + <> = crypto:sha_mac(Key, Data), + ServerDigest = lists:flatten(io_lib:format("~40.16.0b", [X])), + + {case ServerDigest of + ClientDigest -> true; + _WrongDigest -> "Incorrect digest" + end, + ReqData, State}; + + _NoKey -> + {"No proper provisioning for this device", ReqData, State} + end. + +% JSON: {"memtotal":13572,"version":210,"memcached":3280,"membuffers":1076,"memfree":812,"uptime":17394,"reset":1} +% Mochijson2: {struct,[{<<"memtotal">>, 13572}, +% {<<"version">>, 210}, +% {<<"memcached">>, 3280}, +% {<<"membuffers">>, 1076}, +% {<<"memfree">>, 812}, +% {<<"uptime">>, 17394}, +% {<<"reset">>, 1}]} +process_post(ReqData, #state{device = Device} = State) -> + {data, Result} = mysql:execute(pool, device_props, [Device]), + [[Key, Upgrade, Resets]] = mysql:get_result_rows(Result), + + {struct, JsonData} = mochijson2:decode(wrq:req_body(ReqData)), + + Version = proplists:get_value(<<"version">>, JsonData), + Reset = proplists:get_value(<<"reset">>, JsonData), + Uptime = proplists:get_value(<<"uptime">>, JsonData), + Memtotal = proplists:get_value(<<"memtotal">>, JsonData), + Memcached = proplists:get_value(<<"memcached">>, JsonData), + Membuffers = proplists:get_value(<<"membuffers">>, JsonData), + Memfree = proplists:get_value(<<"memfree">>, JsonData), + + NewResets = Resets + Reset, + + mysql:execute(pool, device_update, + [unix_time(), Version, 0, NewResets, Uptime, Memtotal, Memfree, Memcached, Membuffers, Device]), + + + JsonResponse = mochijson2:encode({struct, [{<<"upgrade">>, Upgrade}, + {<<"timestamp">>, unix_time()} + ]}), + + <> = crypto:sha_mac(Key, JsonResponse), + Digest = lists:flatten(io_lib:format("~40.16.0b", [X])), + + DigestedReqData = wrq:set_resp_header("X-Digest", Digest, ReqData), + EmbodiedReqData = wrq:set_resp_body(JsonResponse, DigestedReqData), + + {true , EmbodiedReqData, State}. diff --git a/server/api/flukso/src/flukso_sensor_xyz.erl b/server/api/flukso/src/flukso_sensor_xyz.erl index d2912fa..1ead5de 100644 --- a/server/api/flukso/src/flukso_sensor_xyz.erl +++ b/server/api/flukso/src/flukso_sensor_xyz.erl @@ -89,7 +89,7 @@ is_authorized(ReqData, State) -> end. is_auth_POST(ReqData, #state{rrdSensor = Sensor, digest = ClientDigest} = State) -> - {data, Result} = mysql:execute(pool, device_key, [Sensor]), + {data, Result} = mysql:execute(pool, sensor_key, [Sensor]), case mysql:get_result_rows(Result) of [[Key]] ->