PHP XML API service
 
_Dejan_1. sep 2011 13:51:34Pridružen od:
16. jun 2010
268 objav
+188-222
#1Pozdravljeni,
Potreboval bi malo pomoči oz. predlogov glede izdelave custom API-ja ki komunicira z mysql bazo in se nahaja na cpanel shared hostingu...
Na ta API se bodo povezovale custom embedded naprave:
72 MHz 32-bit ARM 7 Processor
16MB RAM and 4.5MB FLASH
GPRS/3G Modem

Ker nisem nikoli ničesar programiral v php-ju bi vas prosil če preverite primer kode in poveste kaj ni ok oz. kaj bi vi spremenili...
XML ki bi ga naprava poslala za prijavo v sistem bi bil:<?xml version='1.0' encoding="iso-8859-1" ?>
<packet>
<mac_address>00:00:00:00:00:00</mac_address>
<serial>000000</serial>
<hostname>testdevice</hostname>
<model>devmodel</model>
<sw_version>1.00.0000</sw_version>
<hw_version>1.00</hw_version>
</packet>

Koda login skripte pa bi izgledala takole:<?php
include('config.php');

function getClientIPAddress()
{
if (!empty($_SERVER['HTTP_CLIENT_IP']))
{
$ip=$_SERVER['HTTP_CLIENT_IP'];
}
elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR']))
{
$ip=$_SERVER['HTTP_X_FORWARDED_FOR'];
}
else
{
$ip=$_SERVER['REMOTE_ADDR'];
}
return $ip;
}

$db = mysql_connect($dbhost, $dbuser, $dbpasswd);
mysql_select_db ($dbname) or die ("Cannot connect to database");
mysql_query("SET NAMES 'utf8'");

libxml_use_internal_errors(true);
$request_xml = new SimpleXMLElement($HTTP_RAW_POST_DATA);
if (!$request_xml)
{
echo "Failed loading XML\n";
foreach(libxml_get_errors() as $error)
{
echo "\t", $error->message;
}
}
else
{
$sql = "SELECT id, active, REPLACE(UUID(),'-', '') AS session FROM devices WHERE mac_address='" . $request_xml->mac_address . "' AND serial='" . $request_xml->serial . "' AND hostname='" . $request_xml->hostname . "'";
$result = mysql_query($sql);
$device = mysql_fetch_object($result);

$response_xml = new SimpleXMLElement("<?xml version='1.0' encoding=\"iso-8859-1\" ?><packet></packet>");
$response_xml->addChild('device_id', $device->id);
$response_xml->addChild('active', $device->active);
$response_xml->addChild('session', $device->session);

if(is_null($device->id))
{
header('Content-Type: text/xml');
echo $response_xml->asXML();
}
else
{
if($device->active = 1)
{
mysql_query("UPDATE devices SET status='01', dt_last_online=Now(), session='" . $device->session . "', sw_version='" . $request_xml->sw_version . "', hw_version='" . $request_xml->hw_version . "', ip_address='" . getClientIPAddress() . "' WHERE id=" . $device->id . ";");
mysql_query("INSERT INTO events (dt, device_id, event, response, description) VALUES (Now(), " . $device->id . ", '0001', '01', '" . $device->session . "');");
header('Content-Type: text/xml');
echo $response_xml->asXML();
}
else
{
mysql_query("UPDATE devices SET status='02', dt_last_online=Now(), session='', sw_version='" . $request_xml->sw_version . "', hw_version='" . $request_xml->hw_version . "', ip_address='" . getClientIPAddress() . "' WHERE id=" . $device->id . ";");
mysql_query("INSERT INTO events (dt, device_id, event, response, description) VALUES (Now(), " . $device->id . ", '0001', '02', '');");
header('Content-Type: text/xml');
echo $response_xml->asXML();
}
}
}
mysql_close($db);
?>

Response bo XML dokument ki izgleda takole:<?xml version='1.0' encoding="iso-8859-1" ?>
<packet>
<device_id>25638</device_id>
<status>1</status>
<session>c4cb465cd3a211e087f6006438a934f5</session>
</packet>

Torej kaj bi vi spremenili in zakaj? Kje sem ga lomil? Za kakršnokoli pomoč vam bom hvaležen.
všeč(0)ni všeč(0)spam(0)
 
_Dejan_2. sep 2011 07:54:37Pridružen od:
16. jun 2010
268 objav
+188-222
#2hm ali nihče ne zna pomagati svetovati kako naj se lotim zadeve oz. ali je tak način ok?
Ker ne morem urejati prvega posta bom dodal kodo v kateri sem v večini primerov "preprečil" SQL Injection me pa zanima ali je smiselno dodati še stripslashes() preden kličem mysql_real_escape_string() ???
Nova koda:<?php
include('config.php');

error_reporting(0);
libxml_use_internal_errors(true);
libxml_clear_errors();

function getClientIPAddress()
{
if (!empty($_SERVER['HTTP_CLIENT_IP']))
{
$ip=$_SERVER['HTTP_CLIENT_IP'];
}
elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR']))
{
$ip=$_SERVER['HTTP_X_FORWARDED_FOR'];
}
else
{
$ip=$_SERVER['REMOTE_ADDR'];
}
return $ip;
}

$db = mysql_connect($dbhost, $dbuser, $dbpasswd);
mysql_select_db ($dbname) or die ("Cannot connect to database");
mysql_query("SET NAMES 'utf8'");

try {
$request_xml = new SimpleXMLElement($HTTP_RAW_POST_DATA);
} catch (Exception $e) {}

if (!$request_xml)
{
$response_xml = new SimpleXMLElement("<?xml version='1.0' encoding=\"iso-8859-1\" ?><error></error>");
foreach(libxml_get_errors() as $error)
{
$response_xml->addChild('message', trim($error->message));
}
libxml_clear_errors();
header('Content-Type: text/xml');
echo $response_xml->asXML();
}
else
{
$mac_address = mysql_real_escape_string($request_xml->mac_address);
$serial = mysql_real_escape_string($request_xml->serial);
$hostname = mysql_real_escape_string($request_xml->hostname);
$sw_version = mysql_real_escape_string($request_xml->sw_version);
$hw_version = mysql_real_escape_string($request_xml->hw_version);

$sql = "SELECT id, active, REPLACE(UUID(),'-', '') AS session FROM devices WHERE mac_address='" . $mac_address . "' AND serial='" . $serial . "' AND hostname='" . $hostname . "'";
$result = mysql_query($sql);
$device = mysql_fetch_object($result);

$response_xml = new SimpleXMLElement("<?xml version='1.0' encoding=\"iso-8859-1\" ?><packet></packet>");
$response_xml->addChild('device_id', $device->id);
$response_xml->addChild('active', $device->active);
$response_xml->addChild('session', $device->session);

if(is_null($device->id))
{
header('Content-Type: text/xml');
echo $response_xml->asXML();
}
else
{
if($device->active = 1)
{
mysql_query("UPDATE devices SET status='01', dt_last_online=Now(), session='" . $device->session . "', sw_version='" . $sw_version . "', hw_version='" . hw_version . "', ip_address='" . getClientIPAddress() . "' WHERE id=" . $device->id . ";");
mysql_query("INSERT INTO events (dt, device_id, event, response, description) VALUES (Now(), " . $device->id . ", '0001', '01', '" . $device->session . "');");
header('Content-Type: text/xml');
echo $response_xml->asXML();
}
else
{
mysql_query("UPDATE devices SET status='02', dt_last_online=Now(), session='', sw_version='" . $sw_version . "', hw_version='" . hw_version . "', ip_address='" . getClientIPAddress() . "' WHERE id=" . $device->id . ";");
mysql_query("INSERT INTO events (dt, device_id, event, response, description) VALUES (Now(), " . $device->id . ", '0001', '02', '');");
header('Content-Type: text/xml');
echo $response_xml->asXML();
}
}
}
mysql_close($db);
?>
všeč(0)ni všeč(0)spam(0)
 
Roky2. sep 2011 08:12:20Pridružen od:
9. apr 2008
1879 objav
+1475-17783
#3Ne razumem sploh kje je težava. Iščeš koncept ... ali ti dejansko kaj ne dela?
všeč(0)ni všeč(0)spam(0)
 
_Dejan_2. sep 2011 08:24:42Pridružen od:
16. jun 2010
268 objav
+188-222
#4
Roky:
Ne razumem sploh kje je težava. Iščeš koncept ... ali ti dejansko kaj ne dela?

V bistvu zadeva dela, zanima me predvsem če sem glede security-a kje mimo vsekal in je skripta potencialno nevarna, oz. če sem kje kaj spregledal in bo zadeva požirala preveč resoursev. Z 1 clientom to ne morem testirat, ko pa bo laufalo na produkcijskem strežniku in gor 100-200 naprav pa je tudi težko narediti kakšne večje spremembe :)
všeč(0)ni všeč(0)spam(0)
 
Roky2. sep 2011 09:16:09Pridružen od:
9. apr 2008
1879 objav
+1475-17783
#5#1 Validacija
Naredi dodatno validacijo, v smilu intval tam kjer veš da je integer, regex kjer veš da je mac address itd.$mac_address = mysql_real_escape_string($request_xml->mac_address);
$serial = mysql_real_escape_string($request_xml->serial);
$hostname = mysql_real_escape_string($request_xml->hostname);
$sw_version = mysql_real_escape_string($request_xml->sw_version);
$hw_version = mysql_real_escape_string($request_xml->hw_version);

#2 Pri prvem blogu kar dobiš noter HTTP RAW POST XML. Kako pa veš, da ni kakšen čudn xml? Raje ga prej preverivalidateXMLFile($xmlString) {
libxml_use_internal_errors(true);

$doc = new DOMDocument();
$doc->loadXML($xmlString);

$errors = libxml_get_errors();
if (empty($errors)) {
return true;
}

$error = $errors[0];
if ($error->level < 3) {
return true;
}

$errorsMsg = '';
foreach ($errors as $error) {
$errorsMsg .= trim($error->message) . "\n";
}

return $errorsMsg;
}
if ($validationStatus !== true) {
echo 'Napaka z XML-jem, ni validiran';
exit; // al karkol
}

Ostalo je kul.
všeč(+2)ni všeč(0)spam(0)
 
blackmamba2. sep 2011 09:30:59Pridružen od:
4. mar 2008
307 objav
+163-101
#6Lahko bi uvedel kak error reporting sistem, vsaj za beleženje mysql napak.

Pri "UPDATE DEVICES" manjka $ pred hw_versionhw_version='" . hw_version . "'Poglej si še mysql_free_result.

Kako pogosto naprave pošiljajo requeste, boš vedel ti. Če vsaka naprava naredi request 1x na dan ali 1x na sekundo je razlika več kot občutna :)

Če te skrbi, pripravi testno okolje in simuliraj 500 naprav. (mogoče ab)

edit: in tako kot pravi Roky: dodatna validacija regex in intval()!

LP
nazadnje urejal blackmamba 2. sep 2011 09:33:31
všeč(+3)ni všeč(0)spam(0)
 
_Dejan_2. sep 2011 10:43:23Pridružen od:
16. jun 2010
268 objav
+188-222
#7Hvala obema za odgovore.
1.)Kar se tiče validacije sem v kodo dodal funkcije za validacijo:function validate_macaddress($val)
{
if(preg_match('/^([0-9a-fA-F][0-9a-fA-F]:){5}([0-9a-fA-F][0-9a-fA-F])$/', $val))
{
return $val;
}
else
{
return "00:00:00:00:00:00"
}
}

function validate_serial($val)
{
if(preg_match('/^[0-9]{6}$/', $val))
{
return $val;
}
else
{
return "000000"
}
}

function validate_hostname($val)
{
if(preg_match('/(?=^.{1,254}$)(^(?:(?!\d+\.|-)[a-zA-Z0-9_\-]{1,63}(?<!-)\.?)+(?:[a-zA-Z]{2,})$)/', $val))
{
return $val;
}
else
{
return "default"
}
}

function validate_sw_version($val)
{
if(preg_match('/^(\d+)((\.{1}\d+)*)(\.{0})$/', $val))
{
return $val;
}
else
{
return "0.00.0000"
}
}

function validate_hw_version($val)
{
if(preg_match('/^(\d+)((\.{1}\d+)*)(\.{0})$/', $val))
{
return $val;
}
else
{
return "0.00"
}
}

In nato spremenil validacijo spremeljivk:$mac_address = mysql_real_escape_string(validate_macaddress($request_xml->mac_address));
$serial = mysql_real_escape_string(validate_serial($request_xml->serial));
$hostname = mysql_real_escape_string(validate_hostname($request_xml->hostname));
$sw_version = mysql_real_escape_string(validate_sw_version($request_xml->sw_version));
$hw_version = mysql_real_escape_string(validate_hw_version($request_xml->hw_version));

2.)Bom dodal preverjanje XML-ja.
3.)Sem popravil manjkajoči $ v query-u in na koncu predno zaprem bazo dodal mysql_free_result().
4.)login requestov bo zelo malo oz. samo takrat ko bo naprava izgubila povezavo s strežnikom ali se resetirala. Bodo pa še drugi requesti in sicer pošiljanje statusa naprave ki bo vsakih 10-30s(Odvisno od konfiguracije) in pa pošiljanje statusa modula priklopljenega na napravo(max. 4 moduli) vsakih 5-20sekund. dosti manjši interval pomoje itak ni možen zaradi samega pinga gprs/3g omrežja :) Ti xml-ji bodo tudi malo večji kot login ker bodo vsebovali več podatkov...
všeč(0)ni všeč(0)spam(0)
 
_Dejan_2. sep 2011 12:43:09Pridružen od:
16. jun 2010
268 objav
+188-222
#8@Roky: Sem šele zdele opazil glede validateXMLFile ...
v kodi že preverim če je struktura XML-ja pravilna: try {
$request_xml = new SimpleXMLElement($HTTP_RAW_POST_DATA);
} catch (Exception $e) {}

if (!$request_xml)
{
//NAPAKA XML NIMA PRAVILNE STRUKTURE IN KOT REZULTAT VRNE ERROR XML DOKUMENT
$response_xml = new SimpleXMLElement("<?xml version='1.0' encoding=\"iso-8859-1\" ?><error></error>");
foreach(libxml_get_errors() as $error)
{
$response_xml->addChild('message', trim($error->message));
}
libxml_clear_errors();
header('Content-Type: text/xml');
echo $response_xml->asXML();
}
else
{
//XML STRUKTURA JE OK
}
Če pogledaš sem imel to narejeno v drugem postu... tako da trenutna koda izgleda takole:<?php
include('config.php');

libxml_use_internal_errors(true);
libxml_clear_errors();

function getClientIPAddress()
{
if (!empty($_SERVER['HTTP_CLIENT_IP']))
{
$ip=$_SERVER['HTTP_CLIENT_IP'];
}
elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR']))
{
$ip=$_SERVER['HTTP_X_FORWARDED_FOR'];
}
else
{
$ip=$_SERVER['REMOTE_ADDR'];
}
return $ip;
}

function validate_macaddress($val)
{
if(preg_match('/^([0-9a-fA-F][0-9a-fA-F]:){5}([0-9a-fA-F][0-9a-fA-F])$/', $val))
{
return $val;
}
else
{
return "00:00:00:00:00:00";
}
}

function validate_serial($val)
{
if(preg_match('/^[0-9]{6}$/', $val))
{
return $val;
}
else
{
return "000000";
}
}

function validate_hostname($val)
{
if(preg_match('/(?=^.{1,254}$)(^(?:(?!\d+\.|-)[a-zA-Z0-9_\-]{1,63}(?<!-)\.?)+(?:[a-zA-Z]{2,})$)/', $val))
{
return $val;
}
else
{
return "default";
}
}

function validate_sw_version($val)
{
if(preg_match('/^(\d+)((\.{1}\d+)*)(\.{0})$/', $val))
{
return $val;
}
else
{
return "0.00.0000";
}
}

function validate_hw_version($val)
{
if(preg_match('/^(\d+)((\.{1}\d+)*)(\.{0})$/', $val))
{
return $val;
}
else
{
return "0.00";
}
}

$db = mysql_connect($dbhost, $dbuser, $dbpasswd);
mysql_select_db ($dbname) or die ("Cannot connect to database");
mysql_query("SET NAMES 'utf8'");

try {
$request_xml = new SimpleXMLElement($HTTP_RAW_POST_DATA);
} catch (Exception $e) {}

if (!$request_xml)
{
$response_xml = new SimpleXMLElement("<?xml version='1.0' encoding=\"iso-8859-1\" ?><error></error>");
foreach(libxml_get_errors() as $error)
{
$response_xml->addChild('message', trim($error->message));
}
libxml_clear_errors();
header('Content-Type: text/xml');
echo $response_xml->asXML();
}
else
{
$mac_address = mysql_real_escape_string(validate_macaddress($request_xml->mac_address));
$serial = mysql_real_escape_string(validate_serial($request_xml->serial));
$hostname = mysql_real_escape_string(validate_hostname($request_xml->hostname));
$sw_version = mysql_real_escape_string(validate_sw_version($request_xml->sw_version));
$hw_version = mysql_real_escape_string(validate_hw_version($request_xml->hw_version));

$sql = "SELECT id, active, REPLACE(UUID(),'-', '') AS session FROM devices WHERE mac_address='" . $mac_address . "' AND serial='" . $serial . "' AND hostname='" . $hostname . "'";
$result = mysql_query($sql);
$device = mysql_fetch_object($result);

$response_xml = new SimpleXMLElement("<?xml version='1.0' encoding=\"iso-8859-1\" ?><packet></packet>");
$response_xml->addChild('device_id', $device->id);
$response_xml->addChild('active', $device->active);
$response_xml->addChild('session', $device->session);

if(is_null($device->id))
{}
else
{
if($device->active = 1)
{
mysql_query("UPDATE devices SET status='01', dt_last_online=Now(), session='" . $device->session . "', sw_version='" . $sw_version . "', hw_version='" . $hw_version . "', ip_address='" . getClientIPAddress() . "' WHERE id=" . $device->id . ";");
mysql_query("INSERT INTO events (dt, device_id, event, response, description) VALUES (Now(), " . $device->id . ", '0001', '01', '" . $device->session . "');");
}
else
{
mysql_query("UPDATE devices SET status='02', dt_last_online=Now(), session='', sw_version='" . $sw_version . "', hw_version='" . $hw_version . "', ip_address='" . getClientIPAddress() . "' WHERE id=" . $device->id . ";");
mysql_query("INSERT INTO events (dt, device_id, event, response, description) VALUES (Now(), " . $device->id . ", '0001', '02', '');");
}
}
header('Content-Type: text/xml');
echo $response_xml->asXML();
}
mysql_free_result($result);
mysql_close($db);
?>
nazadnje urejal _Dejan_ 2. sep 2011 12:57:31
všeč(0)ni všeč(0)spam(0)
 
Roky2. sep 2011 16:37:25Pridružen od:
9. apr 2008
1879 objav
+1475-17783
#9Aha, OK, pol je pa to to.
všeč(0)ni všeč(0)spam(0)
 
_Dejan_28. nov 2011 14:35:31Pridružen od:
16. jun 2010
268 objav
+188-222
#10Imam še eno vprašanje in sicer preko socketa pošljem serverju request:POST /API/ HTTP/1.0
Host: www.domena.com
Content-type: text/xml
Content-length: 116
Connection: Close

<UserAuthOn><IdentType value='KEYBOARD'/><IdentCode value='12345678'/><Tstamp value='1977-1-1 1:1:37'/></UserAuthOn>

Vendar ne dobim pravilne povratne informacije. Od serverja dobim:HTTP/1.1 200 OK
Server: nginx
Date: Mon, 28 Nov 2011 13:23:55 GMT
Content-Type: text/xml
Connection: close
X-Powered-By: PHP/5.3.8

Torej brez "Content-Length:" in vsebine. Preko Firefox REST Clienta dela BP. Kakšna ideja kaj je narobe v mojem requestu?
všeč(0)ni všeč(0)spam(0)
 
stran 1 od 2 |<<12>|