| 1 | <?php | 
|---|
| 2 | /** | 
|---|
| 3 | * @file    index.php | 
|---|
| 4 | * @brief   OpenGnsys REST API: common functions and routes | 
|---|
| 5 | * @warning All input and output messages are formatted in JSON. | 
|---|
| 6 | * @note    Some ideas are based on article "How to create REST API for Android app using PHP, Slim and MySQL" by Ravi Tamada, thanx. | 
|---|
| 7 | * @license GNU GPLv3+ | 
|---|
| 8 | * @author  Ramón M. Gómez, ETSII Univ. Sevilla | 
|---|
| 9 | * @version 1.1.0 - First version | 
|---|
| 10 | * @date    2016-11-17 | 
|---|
| 11 | */ | 
|---|
| 12 |  | 
|---|
| 13 |  | 
|---|
| 14 | // Common functions. | 
|---|
| 15 |  | 
|---|
| 16 | /** | 
|---|
| 17 | * @brief   Compose JSON response. | 
|---|
| 18 | * @param   int status      Status code for HTTP response. | 
|---|
| 19 | * @param   array response  Response data. | 
|---|
| 20 | * @param   int opts        Options to encode JSON data. | 
|---|
| 21 | * @return  string          JSON response. | 
|---|
| 22 | */ | 
|---|
| 23 | function jsonResponse($status, $response, $opts=0) { | 
|---|
| 24 | $app = \Slim\Slim::getInstance(); | 
|---|
| 25 | // HTTP status code. | 
|---|
| 26 | $app->status($status); | 
|---|
| 27 | // Content-type HTTP header. | 
|---|
| 28 | $app->contentType('application/json; charset=utf-8'); | 
|---|
| 29 | // JSON response. | 
|---|
| 30 | echo json_encode($response, $opts); | 
|---|
| 31 | } | 
|---|
| 32 |  | 
|---|
| 33 | /** | 
|---|
| 34 | * @brief   Print immediately JSON response to continue processing. | 
|---|
| 35 | * @param   int status      Status code for HTTP response. | 
|---|
| 36 | * @param   array response  Response data. | 
|---|
| 37 | * @param   int opts        Options to encode JSON data. | 
|---|
| 38 | * @return  string          JSON response. | 
|---|
| 39 | */ | 
|---|
| 40 | function jsonResponseNow($status, $response, $opts=0) { | 
|---|
| 41 | // Flush buffer. | 
|---|
| 42 | ob_end_clean(); | 
|---|
| 43 | ob_end_flush(); | 
|---|
| 44 | header("Connection: close"); | 
|---|
| 45 | // Compose headers and content. | 
|---|
| 46 | http_response_code((int)$status); | 
|---|
| 47 | header('Content-type: application/json; charset=utf-8'); | 
|---|
| 48 | ignore_user_abort(); | 
|---|
| 49 | ob_start(); | 
|---|
| 50 | echo json_encode($response, $opts); | 
|---|
| 51 | $size = ob_get_length(); | 
|---|
| 52 | header("Content-Length: $size"); | 
|---|
| 53 | // Print content. | 
|---|
| 54 | ob_end_flush(); | 
|---|
| 55 | flush(); | 
|---|
| 56 | session_write_close(); | 
|---|
| 57 | } | 
|---|
| 58 |  | 
|---|
| 59 | /** | 
|---|
| 60 | * @brief    Validate API key included in "Authorization" HTTP header. | 
|---|
| 61 | * @return   JSON response on error. | 
|---|
| 62 | */ | 
|---|
| 63 | function validateApiKey() { | 
|---|
| 64 | global $cmd; | 
|---|
| 65 | global $userid; | 
|---|
| 66 | $response = array(); | 
|---|
| 67 | $app = \Slim\Slim::getInstance(); | 
|---|
| 68 | // Read Authorization HTTP header. | 
|---|
| 69 | if (! empty($_SERVER['HTTP_AUTHORIZATION'])) { | 
|---|
| 70 | // Assign user id. that match this key to global variable. | 
|---|
| 71 | $apikey = htmlspecialchars($_SERVER['HTTP_AUTHORIZATION']); | 
|---|
| 72 | $cmd->texto = "SELECT idusuario | 
|---|
| 73 | FROM usuarios | 
|---|
| 74 | WHERE apikey='$apikey' LIMIT 1"; | 
|---|
| 75 | $rs=new Recordset; | 
|---|
| 76 | $rs->Comando=&$cmd; | 
|---|
| 77 | if ($rs->Abrir()) { | 
|---|
| 78 | $rs->Primero(); | 
|---|
| 79 | if (!$rs->EOF){ | 
|---|
| 80 | // Fetch user id. | 
|---|
| 81 | $userid = $rs->campos["idusuario"]; | 
|---|
| 82 | } else { | 
|---|
| 83 | // Credentials error. | 
|---|
| 84 | $response['message'] = 'Login failed. Incorrect credentials'; | 
|---|
| 85 | jsonResponse(401, $response); | 
|---|
| 86 | $app->stop(); | 
|---|
| 87 | } | 
|---|
| 88 | $rs->Cerrar(); | 
|---|
| 89 | } else { | 
|---|
| 90 | // Access error. | 
|---|
| 91 | $response['message'] = "An error occurred, please try again"; | 
|---|
| 92 | jsonResponse(500, $response); | 
|---|
| 93 | } | 
|---|
| 94 | } else { | 
|---|
| 95 | // Error: missing API key. | 
|---|
| 96 | $response['message'] = 'Missing API key'; | 
|---|
| 97 | jsonResponse(400, $response); | 
|---|
| 98 | $app->stop(); | 
|---|
| 99 | } | 
|---|
| 100 | } | 
|---|
| 101 |  | 
|---|
| 102 | /** | 
|---|
| 103 | * @brief    Check if parameter is set and print error messages if empty. | 
|---|
| 104 | * @param    string param    Parameter to check. | 
|---|
| 105 | * @return   boolean         "false" if parameter is null, otherwise "true". | 
|---|
| 106 | */ | 
|---|
| 107 | function checkParameter($param) { | 
|---|
| 108 | if (isset($param)) { | 
|---|
| 109 | return true; | 
|---|
| 110 | } else { | 
|---|
| 111 | // Print error message. | 
|---|
| 112 | $response['message'] = 'Parameter not found'; | 
|---|
| 113 | jsonResponse(400, $response); | 
|---|
| 114 | return false; | 
|---|
| 115 | } | 
|---|
| 116 | } | 
|---|
| 117 |  | 
|---|
| 118 | /** | 
|---|
| 119 | * @brief    Check if all parameters are positive integer numbers. | 
|---|
| 120 | * @param    int id ...      Identificators to check (variable number of parameters). | 
|---|
| 121 | * @return   boolean         "true" if all ids are int>0, otherwise "false". | 
|---|
| 122 | */ | 
|---|
| 123 | function checkIds() { | 
|---|
| 124 | $opts = Array('options' => Array('min_range' => 1));    // Check for int>0 | 
|---|
| 125 | foreach (func_get_args() as $id) { | 
|---|
| 126 | if (!filter_var($id, FILTER_VALIDATE_INT, $opts)) { | 
|---|
| 127 | return false; | 
|---|
| 128 | } | 
|---|
| 129 | } | 
|---|
| 130 | return true; | 
|---|
| 131 | } | 
|---|
| 132 |  | 
|---|
| 133 | /** | 
|---|
| 134 | * @fn       sendCommand($serverip, $serverport, $reqframe, &$values) | 
|---|
| 135 | * @brief    Send a command to an OpenGnsys ogAdmServer and get request. | 
|---|
| 136 | * @param    string serverip    Server IP address. | 
|---|
| 137 | * @param    string serverport  Server port. | 
|---|
| 138 | * @param    string reqframe    Request frame (field's separator is "\r"). | 
|---|
| 139 | * @param    array values       Response values (out parameter). | 
|---|
| 140 | * @return   boolean            "true" if success, otherwise "false". | 
|---|
| 141 | */ | 
|---|
| 142 | function sendCommand($serverip, $serverport, $reqframe, &$values) { | 
|---|
| 143 | global $LONCABECERA; | 
|---|
| 144 | global $LONHEXPRM; | 
|---|
| 145 |  | 
|---|
| 146 | // Connect to server. | 
|---|
| 147 | $respvalues = ""; | 
|---|
| 148 | $connect = new SockHidra($serverip, $serverport); | 
|---|
| 149 | if ($connect->conectar()) { | 
|---|
| 150 | // Send request frame to server. | 
|---|
| 151 | $result = $connect->envia_peticion($reqframe); | 
|---|
| 152 | if ($result) { | 
|---|
| 153 | // Parse request frame. | 
|---|
| 154 | $respframe = $connect->recibe_respuesta(); | 
|---|
| 155 | $connect->desconectar(); | 
|---|
| 156 | $paramlen = hexdec(substr($respframe, $LONCABECERA, $LONHEXPRM)); | 
|---|
| 157 | $params = substr($respframe, $LONCABECERA+$LONHEXPRM, $paramlen); | 
|---|
| 158 | // Fetch values and return result. | 
|---|
| 159 | $values = extrae_parametros($params, "\r", '='); | 
|---|
| 160 | return ($values); | 
|---|
| 161 | } else { | 
|---|
| 162 | // Return with error. | 
|---|
| 163 | return (false); | 
|---|
| 164 | } | 
|---|
| 165 | } else { | 
|---|
| 166 | // Return with error. | 
|---|
| 167 | return (false); | 
|---|
| 168 | } | 
|---|
| 169 | } | 
|---|
| 170 |  | 
|---|
| 171 | /** | 
|---|
| 172 | * @brief   Show custom message for "not found" error (404). | 
|---|
| 173 | */ | 
|---|
| 174 | $app->notFound(function() { | 
|---|
| 175 | echo "REST route not found."; | 
|---|
| 176 | } | 
|---|
| 177 | ); | 
|---|
| 178 |  | 
|---|
| 179 | /** | 
|---|
| 180 | * @brief   Hook to write an error log message. | 
|---|
| 181 | * @warning Message will be written in web server's error file. | 
|---|
| 182 | */ | 
|---|
| 183 | $app->hook('slim.after', function() use ($app) { | 
|---|
| 184 | if ($app->response->getStatus() != 200 ) { | 
|---|
| 185 | // Compose error message (truncating long lines). | 
|---|
| 186 | $app->log->error(date(DATE_ISO8601) . ': ' . | 
|---|
| 187 | $app->getName() . ' ' . | 
|---|
| 188 | $app->response->getStatus() . ': ' . | 
|---|
| 189 | $app->request->getMethod() . ' ' . | 
|---|
| 190 | $app->request->getPathInfo() . ': ' . | 
|---|
| 191 | substr($app->response->getBody(), 0, 100)); | 
|---|
| 192 | } | 
|---|
| 193 | } | 
|---|
| 194 | ); | 
|---|
| 195 |  | 
|---|
| 196 |  | 
|---|
| 197 | // Common routes. | 
|---|
| 198 |  | 
|---|
| 199 | /** | 
|---|
| 200 | * @brief    Get general server information | 
|---|
| 201 | * @note     Route: /info, Method: GET | 
|---|
| 202 | * @param    no | 
|---|
| 203 | * @return   JSON object with basic server information (version, services, etc.) | 
|---|
| 204 | */ | 
|---|
| 205 | $app->get('/info', function() { | 
|---|
| 206 | // Reading version file. | 
|---|
| 207 | @list($project, $version, $release) = explode(' ', file_get_contents('/opt/opengnsys/doc/VERSION.txt')); | 
|---|
| 208 | $response['project'] = trim($project); | 
|---|
| 209 | $response['version'] = trim($version); | 
|---|
| 210 | $response['release'] = trim($release); | 
|---|
| 211 | // Getting actived services. | 
|---|
| 212 | @$services = parse_ini_file('/etc/default/opengnsys'); | 
|---|
| 213 | $response['services'] = Array(); | 
|---|
| 214 | if (@$services["RUN_OGADMSERVER"] === "yes") { | 
|---|
| 215 | array_push($response['services'], "server"); | 
|---|
| 216 | $hasOglive = true; | 
|---|
| 217 | } | 
|---|
| 218 | if (@$services["RUN_OGADMREPO"] === "yes")  array_push($response['services'], "repository"); | 
|---|
| 219 | if (@$services["RUN_BTTRACKER"] === "yes")  array_push($response['services'], "tracker"); | 
|---|
| 220 | // Reading installed ogLive information file. | 
|---|
| 221 | if ($hasOglive === true) { | 
|---|
| 222 | $data = json_decode(@file_get_contents('/opt/opengnsys/etc/ogliveinfo.json')); | 
|---|
| 223 | if (isset($data->oglive)) { | 
|---|
| 224 | $response['oglive'] = $data->oglive; | 
|---|
| 225 | } | 
|---|
| 226 | } | 
|---|
| 227 | jsonResponse(200, $response); | 
|---|
| 228 | } | 
|---|
| 229 | ); | 
|---|
| 230 |  | 
|---|
| 231 | /** | 
|---|
| 232 | * @brief    Get the server status | 
|---|
| 233 | * @note     Route: /status, Method: GET | 
|---|
| 234 | * @param    no | 
|---|
| 235 | * @return   JSON object with all data collected from server status (RAM, %CPU, etc.). | 
|---|
| 236 | */ | 
|---|
| 237 | $app->get('/status', function() { | 
|---|
| 238 | // Getting memory and CPU information. | 
|---|
| 239 | exec("awk '$1~/Mem/ {print $2}' /proc/meminfo",$memInfo); | 
|---|
| 240 | $memInfo = array("total" => $memInfo[0], "used" => $memInfo[1]); | 
|---|
| 241 | $cpuInfo = exec("awk '$1==\"cpu\" {printf \"%.2f\",($2+$4)*100/($2+$4+$5)}' /proc/stat"); | 
|---|
| 242 | $cpuModel = exec("awk -F: '$1~/model name/ {print $2}' /proc/cpuinfo"); | 
|---|
| 243 | $response["memInfo"] = $memInfo; | 
|---|
| 244 | $response["cpu"] = array("model" => trim($cpuModel), "usage" => $cpuInfo); | 
|---|
| 245 | jsonResponse(200, $response); | 
|---|
| 246 | } | 
|---|
| 247 | ); | 
|---|
| 248 | ?> | 
|---|