diff --git a/include/constants.php b/include/constants.php index c874205b7..32cac3031 100644 --- a/include/constants.php +++ b/include/constants.php @@ -73,4 +73,5 @@ define('USER_MAIL_NOTIFICATION_TABLE', $prefixeTable.'user_mail_notification'); define('TAGS_TABLE', $prefixeTable.'tags'); define('IMAGE_TAG_TABLE', $prefixeTable.'image_tag'); define('PLUGINS_TABLE', $prefixeTable.'plugins'); +define('WEB_SERVICES_ACCESS_TABLE', $prefixeTable.'ws_access'); ?> diff --git a/web_service.php b/web_service.php new file mode 100644 index 000000000..8146e43b6 --- /dev/null +++ b/web_service.php @@ -0,0 +1,487 @@ + 20 ) ? substr($key,0,20) : $key; +$len = strlen($key); +&hash = 0; +if (isset($_get['pos'])) +{ + $hash = (!is_numeric($hash)) ? 0 : $_get['pos']; + $hash = (int) $hash; + $hash = $hash % 12; +} +$query = ' +SELECT * + FROM '.WEB_SERVICES_ACCESS_TABLE.' +;'; +$result = pwg_query($query); + +while ($row = mysql_fetch_array($result)) +{ + if ( substr( md5($row['name']),$hash,$len) == $key ) + { + $len = 0; + continue; + } +} +if ( $len > 0 ) +{ + die('Invalid key!'); +} +// $def = Web service already defined partner access +$def = $row; +// +// Now, the partner will get a reply in time +// +$stat_id = 'Web Service'; +if (isset($_SERVER["HTTP_REFERER"]) and + !eregi($_SERVER["HTTP_HOST"],$_SERVER["HTTP_REFERER"])) +{ + $stats_id = substr($_SERVER["HTTP_REFERER"],7); + $pos = strpos($stats_id,'/'); + $stats_id = ( $pos>0 ) ? substr($stats_id,0,$pos) : $stats_id; +} + + + +// FIXME + + +// Check keywords +// + +// Check requested XML template +// + +// Generate query +// + +// Generate XML +// + +// Log it +// + + + + + +//------------ Main security strategy --------------------- +$partner_id = 'default'; +// Security considerations: HTTP_REFERER and FOPEN +// 1 - FOPEN doesn't update current HTTP_REFERER +// 2 - HTTP_REFERER may be hidden/altered for lot of reasons. +// 3 - By this process, you can log HTTP_REFERER of your partner (not yours). +// 4 - Logging HTTP_REFERER needs declarative procedures in some countries. +// 5 - Following those links can be considered as risky. +// 6 - You can turn off, referer logging by $conf['ws-refback'] = false; +// 7 - In the other hand, your partner may give his key to another web site. +// Above all, this information is just an indication. +// $conf['ws-refback'] : Default value is false. + +if (isset($_SERVER["HTTP_REFERER"]) and + !eregi($_SERVER["HTTP_HOST"],$_SERVER["HTTP_REFERER"])) +{ + $partner_id = substr($_SERVER["HTTP_REFERER"],7); + $pos = strpos($partner_id,'/'); + $partner_id = ( $pos>0 ) ? substr($partner_id,0,$pos) : $partner_id; +} +// $partner_id = Is used to check prohibited REFER site (but not only) +// example: www.prohibited-access.be + +if ( isset($conf['ws-refback']) and $conf['ws-refback']) +{ + $log_id = $partner_id; +} +else +{ + $log_id = ''; // Would be set in time by process end +} +// $log_id = History log information +// examples: forum.phpwebgallery.net +// phpwebgallery.net +// demo.phpwebgallery.net + +// +$partner_id = strtolower($partner_id); +// Prohibited REFER: $partner_id is compared (strtolower). +// +//----------------------------- Is a prohibited refer? +if ( $partner_id !== 'default' ) +{ + // Is Referer a prohibited site? + // Compare requestor site to web service key table + // Found and limit = 0 => die + foreach ( $conf['ws_keys'] as $key => $vkey ) + { + if ( strtolower($vkey['id']) == $partner_id and $vkey['limit'] == 0 ) + { + pwg_log( 'WS Prohibited', 'Req.:'.$type, 'From: ws_keys['.$key.']' ); + die($lang['access_forbiden']); + } + } +} +//----------------------------- Which access he will use? +$access = check_ws_access( $conf['ws_keys'] ); +// given key arg is compared asis (Take care of upper/lower case). + +parse_str($access['force'], $force); +// $force contains all forced arguments +// get requested arguments and apply limits +$force['limit'] = ( isset($access['limit']) ) ? $access['limit'] : + $conf['ws_limit'] ; +$arg = force_arg_ws_limit( $force, $conf['ws_limit'] ); +// $arg contains all retain query arguments + +// Warning about $arg !!! Warning !!! Warning !!! Warning !!! Warning !!! +// specially to MOD developpers : +// FOR SECURITY REASON NEVER USE extract() AGAINST $arg +// ( $arg is like $_GET ) + +if ( is_numeric(isset($arg['cat'])) ) +{ + $arg['cat']=floor($arg['cat']); +} +else +{ + unset($arg['cat']); +} +// AND category_id is concatenated if requested or forced +$cat_criterion = ''; +if ( isset($arg['cat']) and ($arg['cat']) > 0 ) +{ + $cat_criterion = ' AND ic.`category_id` ='.$arg['cat'].' '; +} +//-------------------------------------------- SQL Query statement building +// Has to be tested against a LARGE configuration +// for performance consideration +// and maybe rewrite in some cases. + +// All below has to be check to respect code writing rule convention + + + +$query=' + SELECT DISTINCT (i.`id`), + i.`path` , i.`file` , i.`date_available` , + i.`date_creation`, i.`tn_ext` , i.`name` , + i.`filesize` , i.`storage_category_id` , i.`average_rate`, + i.`comment` , i.`author` , i.`hit` ,i.`width` , + i.`height` + FROM `'.IMAGES_TABLE.'` AS i + INNER JOIN `'.IMAGE_CATEGORY_TABLE.'` + AS ic ON i.`id` = ic.`image_id` + INNER JOIN `'.CATEGORIES_TABLE.'` + AS c ON c.`id` = ic.`category_id` + WHERE c.`status` = \'public\' + AND i.`width` > 0 + AND i.`height` > 0 + AND i.`representative_ext` IS NULL + '.$cat_criterion.' + AND c.`id` NOT IN ('.$user['forbidden_categories'].') '; + +// AND c.`agreed_ws` = \'true\' (Obsolete specification replaced by force) + +$list = ( isset($arg['list']) ) ? $arg['list'] : ''; +$type = $arg['type']; +switch($type) +{ + case ($type === 'random' or $type === 'listcat'): /* Random order */ + $query .= ' ORDER BY RAND() DESC '; + break; + case ($type === 'list'): /* list on MBt & z0rglub request */ + $query .= ' AND i.`id` IN ('.$list.') '; + break; + case $type === 'maxviewed': /* hit > 0 and hit desc order */ + $query .= ' AND i.`hit` > 0 + ORDER BY i.`hit` DESC, RAND() DESC '; + break; + case $type === 'recent': /* recent = Date_available desc order */ + $query .= ' ORDER BY i.`date_available` DESC, RAND() DESC '; + break; + case $type === 'highrated': /* avg_rate > 0 and desc order */ +// French Joke : Cette requete s'appelle officieusement l' "ail_gratte" + $query .= ' AND i.`average_rate` > 0 + ORDER BY i.`average_rate` DESC, RAND() DESC '; + break; + case $type === 'oldest': /* Date_available asc order */ + $query .= ' ORDER BY i.`date_available` ASC, RAND() DESC '; + break; + case $type === 'lessviewed': /* hit asc order */ +// French Joke : Cette requete s'appelle officieusement la "lessive" + $query .= ' ORDER BY i.`hit` ASC, RAND() DESC '; + break; + case $type === 'lowrated': /* avg_rate asc order */ + $query .= ' AND i.`average_rate` IS NOT NULL + ORDER BY i.`average_rate` ASC, RAND() DESC '; + break; + case $type === 'undescribed': /* description missing */ +// US/UK Joke : This request is unofficially named 'indiscribable' horror + $query .= ' AND i.`comment` IS NULL + ORDER BY RAND() DESC '; + break; + case $type === 'unnamed': /* new name missing */ + $query .= ' AND i.`comment` IS NULL + ORDER BY RAND() DESC '; + break; + case $type === 'portraits': /* width < height (portrait oriented) */ + $query .= ' AND `width` < (`height` * 0.95) + ORDER BY RAND() DESC '; + break; + case $type === 'landscapes': /* width > height (landscape oriented) */ + $query .= ' AND `width` > (`height` * 1.05) + ORDER BY RAND() DESC '; + break; + case $type === 'squares': /* width ~ height (square form) */ + $query .= ' AND `width` BETWEEN (`height` * 0.95) + AND (`height` * 1.05) + ORDER BY RAND() DESC '; + break; + default: /* Just say: Goodbye !!! */ + die($lang['access_forbiden']); +} /* End switch */ +$query .= ' LIMIT 0 , '.$arg['limit'].';'; +$result = pwg_query( $query ); +$attributes = array( 'width', 'height', 'author', 'date_creation', + 'date_available', 'hit', 'filesize'); +$xml = ' '; +$hr_nbr = 0; $ns_nbr = 0; $tn_nbr = 0; +if ( $log_id == '') +{ + foreach ( $conf['ws_keys'] as $key => $vkey ) + { + if ( $vkey['id'] == $access['id'] ) + { + $log_id = 'R:#'.$key; + break; + } + } +} +while ( $row = mysql_fetch_array( $result ) ) +{ + $tn_nbr++; + $item = ' 0 ) + { + $hr_nbr++; + $item .= ' hrsrc="'.$high.'"'; + $item .= ' hrwidth="'.$hrsize[0].'"'; + $item .= ' hrheight="'.$hrsize[1].'"'; + } + } + $xml .= $item.' />'; + //-------------------------------------- picture ----- log informations +// request_type ( R:#id_requester ), real_category_id [ request_number ], image_file_name ); + pwg_log( $type.'('.$log_id.')', $row['storage_category_id'].'['.$tn_nbr.']', $row['file'] ); +} +$xml .= ' '; +echo $xml; // Send XML +//---------------------------------------- service ----- log informations +$size = 'tn('.$tn_nbr.')'; // thumbnails +if ( $ns_nbr > 0 ) +{ + $size = '('.$ns_nbr.')'; // pictures +} +if ( $hr_nbr > 0 ) +{ + $size = 'HR('.$hr_nbr.'/'.$tn_nbr.')'; // high res. +} + +//pwg_log( 'Web service', 'Req.:'.$type, 'From:'.$log_id ); + + + + + + + /*-- Web Service function + Which access is correct for this resquest? + Compare requestor key to web service key table + If 'defined' => use that one + If not => use default access + If no 'defined' default => exit + 'defined' : Obviously check period and not only defined access + + Return corresponding access (= an entry from web service key table) + --*/ +function check_ws_access( $ws_keys ) +{ + $partnr = ( isset($_GET['key']) ) ? $_GET['key'] : 'default'; + + foreach ( $ws_keys as $key => $access ) + { + if ( $access['id'] == $partnr ) + { + break; + } + } + if ( $access['id'] !== $partnr ) // Not found? =default. + { + $access = $ws_keys[0]; + if ( $access['id'] !== 'default' ) // Check if it's really default + { // definition + die($lang['access_forbiden']); // No default access + } + } + // Checking Dates... + // Take care of that: my partner can be out of dates + // but via default... Answer is NO. + // With out of date period, a partner can be seen as prohibited partner + // if you want to authorise him/her change $conf['ws_keys'] + + // Tests are done with server local time... + if (isset($access['end']) and date('Y-m-d H:i:s')>$access['end']) + { //-- Access ended? + die($lang['access_forbiden']); //-- BTW prohibited + } + if (isset($access['start']) and date('Y-m-d H:i:s')<$access['start']) + { //-- Access started? + die($lang['access_forbiden']); + } + return $access; +} + + /*-- Web Sevice function + Force global arguments to ensure access restriction + ( access defined in web service key table ) + Considering the default limit as well and prohibited site case + + Return overided request ( overided $_GET ) + --*/ +function force_arg_ws_limit( $use, $default ) +{ + if ( $use['limit'] < 1 ) + { //-- Access deny + die($lang['access_forbiden']); + } + $arg = $_GET; // what is required? + if (!isset($arg['limit'])) + { + $arg['limit']=$use['limit']; + } + if (!is_numeric($arg['limit'])) + { + $arg['limit']=$use['limit']; + } + // ----------- use force arg if they are some + foreach ( $use as $kuse => $vuse ) + { + if ( $kuse !== 'limit' ) + { + $arg[$kuse] = $vuse; + } + } + $arg['limit'] = floor(min($arg['limit'], $use['limit'])); + return $arg; +} +?>