1
0
Fork 0
mirror of https://github.com/Yetangitu/ampache synced 2025-10-04 10:19:25 +02:00

Merge branch 'develop' into feature/customMetadata

This commit is contained in:
René Bigler 2015-10-29 20:36:49 +01:00
commit 5f50341dff
2001 changed files with 8879 additions and 331819 deletions

9
.gitignore vendored
View file

@ -7,10 +7,13 @@ channel/.htaccess
*.sln *.sln
*.v11.suo *.v11.suo
*.suo *.suo
logs logs/
/nbproject/private/ /nbproject/private/
.pc .pc
/tmp tmp/
log/ log/
*~ *~
*# *#
*.log
/lib/vendor/
/lib/components/

View file

@ -3,12 +3,14 @@
use Symfony\CS\FixerInterface; use Symfony\CS\FixerInterface;
$finder = Symfony\CS\Finder\DefaultFinder::create() $finder = Symfony\CS\Finder\DefaultFinder::create()
->exclude('lib/components')
->exclude('lib/vendor')
->exclude('modules') ->exclude('modules')
->exclude('nbproject') ->exclude('nbproject')
->in(__DIR__) ->in(__DIR__)
->in(__DIR__ . '/modules/localplay') ->in(__DIR__ . '/modules/localplay')
->in(__DIR__ . '/modules/catalog') ->in(__DIR__ . '/modules/catalog')
->in(__DIR__ . '/modules/ampacheapi') ->in(__DIR__ . '/modules/plugins')
; ;
return Symfony\CS\Config\Config::create() return Symfony\CS\Config\Config::create()

View file

@ -1,5 +1,7 @@
filter: filter:
excluded_paths: excluded_paths:
- 'lib/components/*'
- 'lib/vendor/*'
- 'modules/*' - 'modules/*'
paths: { } paths: { }
@ -8,6 +10,8 @@ tools:
enabled: true enabled: true
filter: filter:
excluded_paths: excluded_paths:
- 'lib/components/*'
- 'lib/vendor/*'
- 'modules/*' - 'modules/*'
- 'themes/*' - 'themes/*'
paths: { } paths: { }
@ -21,6 +25,8 @@ tools:
excluded_dirs: { } excluded_dirs: { }
filter: filter:
excluded_paths: excluded_paths:
- 'lib/components/*'
- 'lib/vendor/*'
- 'modules/*' - 'modules/*'
paths: { } paths: { }
php_analyzer: php_analyzer:
@ -28,9 +34,13 @@ tools:
extensions: extensions:
- php - php
dependency_paths: dependency_paths:
- 'lib/components/*'
- 'lib/vendor/*'
- 'modules/*' - 'modules/*'
filter: filter:
excluded_paths: excluded_paths:
- 'lib/components/*'
- 'lib/vendor/*'
- 'modules/*' - 'modules/*'
- 'themes/*' - 'themes/*'
paths: { } paths: { }
@ -44,16 +54,22 @@ tools:
- '\bimplement(?:s|ed)?\b' - '\bimplement(?:s|ed)?\b'
filter: filter:
excluded_paths: excluded_paths:
- 'lib/components/*'
- 'lib/vendor/*'
- 'modules/*' - 'modules/*'
paths: { } paths: { }
php_loc: php_loc:
enabled: true enabled: true
excluded_dirs: excluded_dirs:
- 'lib/components/*'
- 'lib/vendor/*'
- 'modules/*' - 'modules/*'
php_cpd: php_cpd:
enabled: true enabled: true
excluded_dirs: { } excluded_dirs: { }
filter: filter:
excluded_paths: excluded_paths:
- 'lib/components/*'
- 'lib/vendor/*'
- 'modules/*' - 'modules/*'
paths: { } paths: { }

View file

@ -4,6 +4,7 @@ php:
- 5.4 - 5.4
- 5.5 - 5.5
- 5.6 - 5.6
- 7.0
before_install: before_install:
- wget http://get.sensiolabs.org/php-cs-fixer.phar - wget http://get.sensiolabs.org/php-cs-fixer.phar

View file

@ -83,7 +83,6 @@ Ampache includes some external modules that carry their own licensing.
* [PHPMailer](https://github.com/PHPMailer/PHPMailer): LGPL v2.1 * [PHPMailer](https://github.com/PHPMailer/PHPMailer): LGPL v2.1
* [jQuery](http://jquery.org): MIT * [jQuery](http://jquery.org): MIT
* [Requests](http://requests.ryanmccue.info): ISC Licensed * [Requests](http://requests.ryanmccue.info): ISC Licensed
* [Whatever:hover](http://www.xs4all.nl/~peterned): LGPL v2.1
* [xbmc-php-rpc](https://github.com/karlrixon/xbmc-php-rpc): GPL v3 * [xbmc-php-rpc](https://github.com/karlrixon/xbmc-php-rpc): GPL v3
* [Dropbox SDK](https://github.com/dropbox/dropbox-sdk-php): MIT * [Dropbox SDK](https://github.com/dropbox/dropbox-sdk-php): MIT
* [jPlayer](http://jplayer.org): MIT * [jPlayer](http://jplayer.org): MIT

View file

@ -21,7 +21,7 @@
*/ */
require_once '../lib/init.php'; require_once '../lib/init.php';
require_once AmpConfig::get('prefix') . '/modules/catalog/local.catalog.php'; require_once AmpConfig::get('prefix') . '/modules/catalog/local/local.catalog.php';
if (!Access::check('interface','100')) { if (!Access::check('interface','100')) {
UI::access_denied(); UI::access_denied();
@ -45,13 +45,12 @@ if (is_array($catalogs) && count($catalogs) == 1 && $_REQUEST['action'] !== 'del
} }
} }
} }
$sse_catalogs = urlencode(serialize($catalogs));
/* Big switch statement to handle various actions */ /* Big switch statement to handle various actions */
switch ($_REQUEST['action']) { switch ($_REQUEST['action']) {
case 'add_to_all_catalogs': case 'add_to_all_catalogs':
$sse_url = AmpConfig::get('web_path') . "/server/sse.server.php?worker=catalog&action=add_to_all_catalogs"; catalog_worker('add_to_all_catalogs');
sse_worker($sse_url);
show_confirmation(T_('Catalog Update started...'), '', AmpConfig::get('web_path') . '/admin/catalog.php', 0, 'confirmation', false); show_confirmation(T_('Catalog Update started...'), '', AmpConfig::get('web_path') . '/admin/catalog.php', 0, 'confirmation', false);
break; break;
case 'add_to_catalog': case 'add_to_catalog':
@ -59,13 +58,11 @@ switch ($_REQUEST['action']) {
break; break;
} }
$sse_url = AmpConfig::get('web_path') . "/server/sse.server.php?worker=catalog&action=add_to_catalog&catalogs=" . $sse_catalogs; catalog_worker('add_to_catalog', $catalogs);
sse_worker($sse_url);
show_confirmation(T_('Catalog Update started...'), '', AmpConfig::get('web_path') . '/admin/catalog.php', 0, 'confirmation', false); show_confirmation(T_('Catalog Update started...'), '', AmpConfig::get('web_path') . '/admin/catalog.php', 0, 'confirmation', false);
break; break;
case 'update_all_catalogs': case 'update_all_catalogs':
$sse_url = AmpConfig::get('web_path') . "/server/sse.server.php?worker=catalog&action=update_all_catalogs"; catalog_worker('update_all_catalogs');
sse_worker($sse_url);
show_confirmation(T_('Catalog Update started...'), '', AmpConfig::get('web_path') . '/admin/catalog.php', 0, 'confirmation', false); show_confirmation(T_('Catalog Update started...'), '', AmpConfig::get('web_path') . '/admin/catalog.php', 0, 'confirmation', false);
break; break;
case 'update_catalog': case 'update_catalog':
@ -73,8 +70,7 @@ switch ($_REQUEST['action']) {
break; break;
} }
$sse_url = AmpConfig::get('web_path') . "/server/sse.server.php?worker=catalog&action=update_catalog&catalogs=" . $sse_catalogs; catalog_worker('update_catalog', $catalogs);
sse_worker($sse_url);
show_confirmation(T_('Catalog Update started...'), '', AmpConfig::get('web_path') . '/admin/catalog.php', 0, 'confirmation', false); show_confirmation(T_('Catalog Update started...'), '', AmpConfig::get('web_path') . '/admin/catalog.php', 0, 'confirmation', false);
break; break;
case 'full_service': case 'full_service':
@ -83,8 +79,7 @@ switch ($_REQUEST['action']) {
break; break;
} }
$sse_url = AmpConfig::get('web_path') . "/server/sse.server.php?worker=catalog&action=full_service&catalogs=" . $sse_catalogs; catalog_worker('full_service', $catalogs);
sse_worker($sse_url);
show_confirmation(T_('Catalog Update started...'), '', AmpConfig::get('web_path') . '/admin/catalog.php', 0, 'confirmation', false); show_confirmation(T_('Catalog Update started...'), '', AmpConfig::get('web_path') . '/admin/catalog.php', 0, 'confirmation', false);
break; break;
case 'delete_catalog': case 'delete_catalog':
@ -136,13 +131,11 @@ switch ($_REQUEST['action']) {
show_confirmation($title,$body,$url); show_confirmation($title,$body,$url);
break; break;
case 'clean_all_catalogs': case 'clean_all_catalogs':
$sse_url = AmpConfig::get('web_path') . "/server/sse.server.php?worker=catalog&action=clean_all_catalogs"; catalog_worker('clean_all_catalogs');
sse_worker($sse_url);
show_confirmation(T_('Catalog Clean started...'), '', AmpConfig::get('web_path') . '/admin/catalog.php', 0, 'confirmation', false); show_confirmation(T_('Catalog Clean started...'), '', AmpConfig::get('web_path') . '/admin/catalog.php', 0, 'confirmation', false);
break; break;
case 'clean_catalog': case 'clean_catalog':
$sse_url = AmpConfig::get('web_path') . "/server/sse.server.php?worker=catalog&action=clean_catalog&catalogs=" . $sse_catalogs; catalog_worker('clean_catalog', $catalogs);
sse_worker($sse_url);
show_confirmation(T_('Catalog Clean started...'), '', AmpConfig::get('web_path') . '/admin/catalog.php', 0, 'confirmation', false); show_confirmation(T_('Catalog Clean started...'), '', AmpConfig::get('web_path') . '/admin/catalog.php', 0, 'confirmation', false);
break; break;
case 'update_catalog_settings': case 'update_catalog_settings':
@ -164,8 +157,7 @@ switch ($_REQUEST['action']) {
break; break;
} }
$sse_url = AmpConfig::get('web_path') . "/server/sse.server.php?worker=catalog&action=update_from&add_path=" . scrub_in($_POST['add_path']) . "&update_path=" . $_POST['update_path']; catalog_worker('update_from', null, $_POST);
sse_worker($sse_url);
show_confirmation(T_('Subdirectory update started...'), '', AmpConfig::get('web_path') . '/admin/catalog.php', 0, 'confirmation', false); show_confirmation(T_('Subdirectory update started...'), '', AmpConfig::get('web_path') . '/admin/catalog.php', 0, 'confirmation', false);
break; break;
case 'add_catalog': case 'add_catalog':
@ -198,9 +190,8 @@ switch ($_REQUEST['action']) {
break; break;
} }
$sse_url = AmpConfig::get('web_path') . "/server/sse.server.php?worker=catalog&action=add_catalog&catalog_id=" . $catalog_id . "&options=" . urlencode(serialize($_POST)); $catalogs[] = $catalog_id;
sse_worker($sse_url); catalog_worker('add_to_catalog', $catalogs, $_POST);
show_confirmation(T_('Catalog Creation started...'), '', AmpConfig::get('web_path') . '/admin/catalog.php', 0, 'confirmation', false); show_confirmation(T_('Catalog Creation started...'), '', AmpConfig::get('web_path') . '/admin/catalog.php', 0, 'confirmation', false);
} else { } else {
require AmpConfig::get('prefix') . UI::find_template('show_add_catalog.inc.php'); require AmpConfig::get('prefix') . UI::find_template('show_add_catalog.inc.php');
@ -258,8 +249,7 @@ switch ($_REQUEST['action']) {
require_once AmpConfig::get('prefix') . UI::find_template('show_edit_catalog.inc.php'); require_once AmpConfig::get('prefix') . UI::find_template('show_edit_catalog.inc.php');
break; break;
case 'gather_media_art': case 'gather_media_art':
$sse_url = AmpConfig::get('web_path') . "/server/sse.server.php?worker=catalog&action=gather_media_art&catalogs=" . $sse_catalogs; catalog_worker('gather_media_art', $catalogs);
sse_worker($sse_url);
show_confirmation(T_('Media Art Search started...'), '', AmpConfig::get('web_path') . '/admin/catalog.php', 0, 'confirmation', false); show_confirmation(T_('Media Art Search started...'), '', AmpConfig::get('web_path') . '/admin/catalog.php', 0, 'confirmation', false);
break; break;
case 'show_catalogs': case 'show_catalogs':

View file

@ -83,7 +83,7 @@ switch ($_REQUEST['action']) {
//FIXME: This whole thing is ugly, even though it works. //FIXME: This whole thing is ugly, even though it works.
$browse->set_sort('count','ASC'); $browse->set_sort('count','ASC');
// This one's a doozy // This one's a doozy
$browse_type = isset($_REQUEST['type']) ? $_REQUEST['type'] : 'song'; $browse_type = isset($_REQUEST['type']) ? $_REQUEST['type'] : 'artist';
$browse->set_simple_browse(false); $browse->set_simple_browse(false);
$browse->save_objects(Tag::get_tags($browse_type, 0, 'name')); // Should add a pager? $browse->save_objects(Tag::get_tags($browse_type, 0, 'name')); // Should add a pager?
$object_ids = $browse->get_saved(); $object_ids = $browse->get_saved();

322
composer.json Normal file
View file

@ -0,0 +1,322 @@
{
"name": "ampache/ampache",
"description": "A web based audio/video streaming application and file manager allowing you to access your music & videos from anywhere, using almost any internet enabled device.",
"homepage": "http://ampache.org",
"keywords": ["php", "music", "video", "player", "stream"],
"type": "project",
"license": "GPL-2.0",
"config":
{
"vendor-dir": "lib/vendor",
"component-dir": "lib/components",
"platform": {
"ext-curl": "1.0",
"ext-gd": "1.0",
"ext-gmp": "1.0",
"ext-dom": "1.0",
"ext-pcre": "1.0",
"ext-spl": "1.0",
"ext-simplexml": "1.0",
"ext-ctype": "1.0",
"ext-date": "1.0",
"ext-iconv": "1.0",
"ext-libxml": "1.0",
"ext-mbstring": "1.0",
"ext-xmlwriter": "1.0",
"ext-xmlreader": "1.0",
"lib-libxml": "2.6.20"
}
},
"require": {
"php": ">=5.3.0",
"wikimedia/composer-merge-plugin": "1.*",
"james-heinrich/getid3": "1.9.*",
"mikealmond/musicbrainz": "0.2.*",
"phpmailer/phpmailer": "5.2.*",
"rmccue/requests": "1.6.*",
"react/react": "0.4.*",
"cboden/ratchet": "0.3.*",
"symfony/event-dispatcher": "2.7.*",
"symfony/routing": "2.7.*",
"symfony/http-foundation": "2.7.*",
"szymach/c-pchart": "1.*",
"evenement/evenement": "2.*",
"happyworm/jplayer": "2.*",
"needim/noty": "2.3.*",
"openid/php-openid": "2.*",
"sabre/dav": "3.*",
"maennchen/zipstream-php": "0.*",
"bshaffer/php-echonest-api": "dev-master",
"components/jquery": "2.0.*",
"components/jqueryui": "1.*",
"vakata/jstree": "3.*",
"components/bootstrap": "3.*",
"aehlke/tag-it": "2.*",
"scaron/prettyphoto": "3.*",
"jeromeetienne/jquery-qrcode": "dev-master",
"carhartl/jquery-cookie": "1.*",
"xdan/datetimepicker": "2.*",
"blueimp/jQuery-File-Upload": "9.*",
"aterrien/jQuery-Knob": "1.2.*",
"pklauzinski/jscroll": "2.*",
"kumailht/responsive-elements": "dev-master"
},
"repositories":
[
{
"type": "package",
"package": {
"name": "bshaffer/php-echonest-api",
"version": "dev-master",
"source": {
"url": "https://github.com/bshaffer/php-echonest-api.git",
"type": "git",
"reference": "master"
}
}
},
{
"type": "package",
"package": {
"name": "jeromeetienne/jquery-qrcode",
"type": "component",
"version": "dev-master",
"source": {
"url": "https://github.com/jeromeetienne/jquery-qrcode.git",
"type": "git",
"reference": "master"
},
"extra": {
"component": {
"scripts": [
"src/jquery.qrcode.js",
"src/qrcode.js"
]
}
},
"require": {
"robloach/component-installer": "*"
}
}
},
{
"type": "package",
"package": {
"name": "carhartl/jquery-cookie",
"type": "component",
"version": "1.4.1",
"source": {
"url": "https://github.com/carhartl/jquery-cookie.git",
"type": "git",
"reference": "v1.4.1"
},
"extra": {
"component": {
"scripts": [
"jquery.cookie.js"
]
}
},
"require": {
"robloach/component-installer": "*"
}
}
},
{
"type": "package",
"package": {
"name": "aehlke/tag-it",
"type": "component",
"version": "2.0",
"source": {
"url": "https://github.com/aehlke/tag-it.git",
"type": "git",
"reference": "v2.0"
},
"extra": {
"component": {
"scripts": [
"js/tag-it.js"
],
"styles": [
"css/jquery.tagit.css"
],
"files": [
"js/tag-it.min.js"
]
}
},
"require": {
"robloach/component-installer": "*"
}
}
},
{
"type": "package",
"package": {
"name": "scaron/prettyphoto",
"type": "component",
"version": "3.1.6",
"source": {
"url": "https://github.com/scaron/prettyphoto.git",
"type": "git",
"reference": "3.1.6"
},
"extra": {
"component": {
"scripts": [
"js/jquery.prettyPhoto.js"
],
"styles": [
"css/prettyPhoto.css"
]
}
},
"require": {
"robloach/component-installer": "*"
}
}
},
{
"type": "package",
"package": {
"name": "xdan/datetimepicker",
"type": "component",
"version": "2.4.5",
"source": {
"url": "https://github.com/xdan/datetimepicker.git",
"type": "git",
"reference": "2.4.5"
},
"extra": {
"component": {
"scripts": [
"jquery.datetimepicker.js"
],
"styles": [
"jquery.datetimepicker.css"
]
}
},
"require": {
"robloach/component-installer": "*"
}
}
},
{
"type": "package",
"package": {
"name": "blueimp/jQuery-File-Upload",
"type": "component",
"version": "9.11.2",
"source": {
"url": "https://github.com/blueimp/jQuery-File-Upload.git",
"type": "git",
"reference": "9.11.2"
},
"extra": {
"component": {
"scripts": [
"js/jquery.fileupload.js",
"js/jquery.iframe-transport.js"
]
}
},
"require": {
"robloach/component-installer": "*"
}
}
},
{
"type": "package",
"package": {
"name": "aterrien/jQuery-Knob",
"type": "component",
"version": "1.2.11",
"source": {
"url": "https://github.com/aterrien/jQuery-Knob.git",
"type": "git",
"reference": "1.2.11"
},
"extra": {
"component": {
"scripts": [
"js/jquery.knob.js"
]
}
},
"require": {
"robloach/component-installer": "*"
}
}
},
{
"type": "package",
"package": {
"name": "pklauzinski/jscroll",
"type": "component",
"version": "2.3.4",
"source": {
"url": "https://github.com/pklauzinski/jscroll.git",
"type": "git",
"reference": "v2.3.4"
},
"extra": {
"component": {
"scripts": [
"jquery.jscroll.js"
],
"files": [
"jquery.jscroll.min.js"
]
}
},
"require": {
"robloach/component-installer": "*"
}
}
},
{
"type": "package",
"package": {
"name": "kumailht/responsive-elements",
"type": "component",
"version": "dev-master",
"source": {
"url": "https://github.com/kumailht/responsive-elements.git",
"type": "git",
"reference": "master"
},
"extra": {
"component": {
"scripts": [
"responsive-elements.js"
]
}
},
"require": {
"robloach/component-installer": "*"
}
}
}
],
"extra": {
"merge-plugin": {
"include": [
"composer.local.json",
"modules/catalog/*/composer.json",
"modules/localplay/*/composer.json",
"modules/plugins/*/composer.json"
],
"recurse": false,
"replace": false,
"merge-extra": false
}
}
}

3253
composer.lock generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -7,8 +7,7 @@
; if this config file is up to date ; if this config file is up to date
; this is compared against a value hard-coded ; this is compared against a value hard-coded
; into the init script ; into the init script
config_version = 30 config_version = 31
;######################################################### ;#########################################################
; Path Vars # ; Path Vars #
@ -506,6 +505,11 @@ art_order = "db,tags,folder,musicbrainz,lastfm,google"
; recommendations and metadata. ; recommendations and metadata.
lastfm_api_key = "d5df942424c71b754e54ce1832505ae2" lastfm_api_key = "d5df942424c71b754e54ce1832505ae2"
; Last.FM API secret
; Set this to your Last.FM api secret to actually use Last.FM for
; scrobbling.
lastfm_api_secret = ""
; Wanted ; Wanted
; Set this to true to enable display missing albums and the ; Set this to true to enable display missing albums and the
; possibility for users to mark it as wanted. ; possibility for users to mark it as wanted.
@ -886,7 +890,7 @@ transcode_player_customize = "true"
; Command configuration. Substitutions will be made as follows: ; Command configuration. Substitutions will be made as follows:
; %FILE% => filename ; %FILE% => filename
; %SAMPLE% => target sample rate ; %BITRATE% => target bit rate
; You can do fancy things like VBR, but consider whether the consequences are ; You can do fancy things like VBR, but consider whether the consequences are
; acceptable in your environment. ; acceptable in your environment.
@ -915,11 +919,11 @@ transcode_input = "-i %FILE%"
; For each output format, you should provide the necessary arguments for ; For each output format, you should provide the necessary arguments for
; your transcode_cmd. ; your transcode_cmd.
; encode_args_TYPE = TRANSCODE_CMD_ARGS ; encode_args_TYPE = TRANSCODE_CMD_ARGS
encode_args_mp3 = "-vn -b:a %SAMPLE%K -c:a libmp3lame -f mp3 pipe:1" encode_args_mp3 = "-vn -b:a %BITRATE%K -c:a libmp3lame -f mp3 pipe:1"
encode_args_ogg = "-vn -b:a %SAMPLE%K -c:a libvorbis -f ogg pipe:1" encode_args_ogg = "-vn -b:a %BITRATE%K -c:a libvorbis -f ogg pipe:1"
encode_args_m4a = "-vn -b:a %SAMPLE%K -c:a libfdk_aac -f adts pipe:1" encode_args_m4a = "-vn -b:a %BITRATE%K -c:a libfdk_aac -f adts pipe:1"
encode_args_wav = "-vn -b:a %SAMPLE%K -c:a pcm_s16le -f wav pipe:1" encode_args_wav = "-vn -b:a %BITRATE%K -c:a pcm_s16le -f wav pipe:1"
encode_args_flv = "-b:a %SAMPLE%K -ar 44100 -ac 2 -v 0 -f flv -c:v libx264 -preset superfast -threads 0 pipe:1" encode_args_flv = "-b:a %BITRATE%K -ar 44100 -ac 2 -v 0 -f flv -c:v libx264 -preset superfast -threads 0 pipe:1"
encode_args_webm = "-q %QUALITY% -f webm -c:v libvpx -maxrate %MAXBITRATE%k -preset superfast -threads 0 pipe:1" encode_args_webm = "-q %QUALITY% -f webm -c:v libvpx -maxrate %MAXBITRATE%k -preset superfast -threads 0 pipe:1"
encode_args_ts = "-q %QUALITY% -s %RESOLUTION% -f mpegts -c:v libx264 -c:a libmp3lame -maxrate %MAXBITRATE%k -preset superfast -threads 0 pipe:1" encode_args_ts = "-q %QUALITY% -s %RESOLUTION% -f mpegts -c:v libx264 -c:a libmp3lame -maxrate %MAXBITRATE%k -preset superfast -threads 0 pipe:1"
@ -1020,3 +1024,14 @@ encode_ss_duration = "-t %DURATION%"
; SMTP Password ; SMTP Password
; your mail auth password. ; your mail auth password.
;mail_auth_pass = "" ;mail_auth_pass = ""
;#############################
; Abbreviation Filter #
;#############################
; For file name parsing. Any unecessary abbreviations in file names can be removed during parsing
; by adding them to the list below so that they will be removed during the parsing process.
common_abbr = "divx,xvid,dvdrip,hdtv,lol,axxo,repack,xor,pdtv,
real,vtv,caph,2hd,proper,fqm,uncut,topaz,tvt,notv,fpn,fov,orenji,0tv,
omicron,dsr,ws,sys,crimson,wat,hiqt,internal,brrip,boheme,vost,vostfr,
fastsub,addiction,x264,LOL,720p,1080p,YIFY,evolve,fihtv,first,bokutox,bluray,
tvboom,info"

View file

@ -3,7 +3,7 @@
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Plugins are placed in modules/plugins; the name of the file must be Plugins are placed in modules/plugins; the name of the file must be
<Name>.plugin.php, e.g. Dummy.plugin.php. The file must declare a <Name>/<Name>.plugin.php, e.g. Dummay/Dummy.plugin.php. The file must declare a
corresponding class and the name of the class must be prefixed with corresponding class and the name of the class must be prefixed with
Ampache, e.g. AmpacheDummy. Ampache, e.g. AmpacheDummy.

View file

@ -113,8 +113,8 @@ if (!$typeManaged) {
$defaultimg .= "blankmovie.png"; $defaultimg .= "blankmovie.png";
break; break;
default: default:
$mime = 'image/jpeg'; $mime = 'image/png';
$defaultimg .= "blankalbum.jpg"; $defaultimg .= "blankalbum.png";
break; break;
} }
$image = file_get_contents($defaultimg); $image = file_get_contents($defaultimg);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

BIN
images/blankalbum.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

View file

@ -1,10 +1,16 @@
# Apache 2.4 # Apache 2.4
<IfModule mod_authz_core.c> <IfModule mod_authz_core.c>
Require all denied Require all denied
<FilesMatch "\.css$|\.js$|\.png$|\.gif$|\.jpg$|\.swf$|\.ttf$|\.json$|\.xml$|\.htc$|\.map$|\.woff$">
Require all granted
</FilesMatch>
</IfModule> </IfModule>
# Apache 2.2 # Apache 2.2
<IfModule mod_access.c> <IfModule mod_access.c>
Order deny,allow Order deny,allow
Deny from all Deny from all
<Files ~ "\.css$|\.js$|\.png$|\.gif$|\.jpg$|\.swf$|\.ttf$|\.json$|\.xml$|\.htc$|\.map$|\.woff$">
Allow from all
</Files>
</IfModule> </IfModule>

View file

@ -77,7 +77,10 @@ function get_media_files($media_ids)
function send_zip($name, $media_files) function send_zip($name, $media_files)
{ {
/* Require needed library */ /* Require needed library */
require_once AmpConfig::get('prefix') . '/modules/ZipStream/ZipStream.php'; if (!@include_once(AmpConfig::get('prefix') . '/lib/vendor/maennchen/zipstream-php/src/ZipStream.php')) {
throw new Exception('Missing ZipStream dependency.');
}
$arc = new ZipStream\ZipStream($name . ".zip" ); $arc = new ZipStream\ZipStream($name . ".zip" );
$options = array( $options = array(
'comment' => AmpConfig::get('file_zip_comment'), 'comment' => AmpConfig::get('file_zip_comment'),

View file

@ -232,42 +232,44 @@ class Ampache_RSS
foreach ($data as $item) { foreach ($data as $item) {
$client = new User($item['user']); $client = new User($item['user']);
$song = new Song($item['object_id']); $song = new Song($item['object_id']);
$song->format(); if ($song->enabled) {
$amount = intval(time() - $item['date']+2); $song->format();
$final = '0'; $amount = intval(time() - $item['date']+2);
$time_place = '0'; $final = '0';
while ($amount >= 1) { $time_place = '0';
$final = $amount; while ($amount >= 1) {
$time_place++; $final = $amount;
if ($time_place <= 2) { $time_place++;
$amount = floor($amount/60); if ($time_place <= 2) {
} $amount = floor($amount/60);
if ($time_place == '3') { }
$amount = floor($amount/24); if ($time_place == '3') {
} $amount = floor($amount/24);
if ($time_place == '4') { }
$amount = floor($amount/7); if ($time_place == '4') {
} $amount = floor($amount/7);
if ($time_place == '5') { }
$amount = floor($amount/4); if ($time_place == '5') {
} $amount = floor($amount/4);
if ($time_place == '6') { }
$amount = floor ($amount/12); if ($time_place == '6') {
} $amount = floor ($amount/12);
if ($time_place > '6') { }
$final = $amount . '+'; if ($time_place > '6') {
break; $final = $amount . '+';
} break;
} // end while }
} // end while
$time_string = $final . ' ' . $time_unit[$time_place]; $time_string = $final . ' ' . $time_unit[$time_place];
$xml_array = array('title'=>$song->f_title . ' - ' . $song->f_artist . ' - ' . $song->f_album, $xml_array = array('title'=>$song->f_title . ' - ' . $song->f_artist . ' - ' . $song->f_album,
'link'=>str_replace('&amp;', '&', $song->link), 'link'=>str_replace('&amp;', '&', $song->link),
'description'=>$song->title . ' - ' . $song->f_artist_full . ' - ' . $song->f_album_full . ' - ' . $time_string, 'description'=>$song->title . ' - ' . $song->f_artist_full . ' - ' . $song->f_album_full . ' - ' . $time_string,
'comments'=>$client->username, 'comments'=>$client->username,
'pubDate'=>date("r",$item['date'])); 'pubDate'=>date("r",$item['date']));
$results[] = $xml_array; $results[] = $xml_array;
}
} // end foreach } // end foreach
return $results; return $results;
@ -344,18 +346,20 @@ class Ampache_RSS
$shout = new Shoutbox($id); $shout = new Shoutbox($id);
$shout->format(); $shout->format();
$object = Shoutbox::get_object($shout->object_type, $shout->object_id); $object = Shoutbox::get_object($shout->object_type, $shout->object_id);
$object->format(); if ($object !== null) {
$user = new User($shout->user); $object->format();
$user->format(); $user = new User($shout->user);
$user->format();
$xml_array = array('title' => $user->username . ' ' . T_('on') . ' ' . $object->get_fullname(), $xml_array = array('title' => $user->username . ' ' . T_('on') . ' ' . $object->get_fullname(),
'link' => $object->link, 'link' => $object->link,
'description' => $shout->text, 'description' => $shout->text,
'image' => Art::url($shout->object_id, $shout->object_type, null, 2), 'image' => Art::url($shout->object_id, $shout->object_type, null, 2),
'comments' => '', 'comments' => '',
'pubDate' => date("c", $shout->date) 'pubDate' => date("c", $shout->date)
); );
$results[] = $xml_array; $results[] = $xml_array;
}
} // end foreach } // end foreach
return $results; return $results;

View file

@ -705,6 +705,38 @@ class Api
echo XML_Data::songs($results); echo XML_Data::songs($results);
} // search_songs } // search_songs
/**
* advanced_search
* Perform an advanced search given passed rules
* @param array $input
*/
public static function advanced_search($input)
{
ob_end_clean();
XML_Data::set_offset($input['offset']);
XML_Data::set_limit($input['limit']);
$results = Search::run($input);
$type = 'song';
if (isset($input['type'])) {
$type = $input['type'];
}
switch ($type) {
case 'artist':
echo XML_Data::artists($results);
break;
case 'album':
echo XML_Data::albums($results);
break;
default:
echo XML_Data::songs($results);
break;
}
} // advanced_search
/** /**
* videos * videos
* This returns video objects! * This returns video objects!
@ -977,5 +1009,31 @@ class Api
debug_event('api', 'Sociable feature is not enabled.', 3); debug_event('api', 'Sociable feature is not enabled.', 3);
} }
} // last_shouts } // last_shouts
/**
* rate
* This rate a library item
* @param array $input
*/
public static function rate($input)
{
ob_end_clean();
$type = $input['type'];
$id = $input['id'];
$rating = $input['rating'];
if (!Core::is_library_item($type) || !$id) {
echo XML_Data::error('401', T_('Wrong library item type.'));
} else {
$item = new $type($id);
if (!$item->id) {
echo XML_Data::error('404', T_('Library item not found.'));
} else {
$r = new Rating($id, $type);
$r->set_rating($rating);
echo XML_Data::single_string('success');
}
}
} // rate
} // API class } // API class

View file

@ -21,7 +21,7 @@
*/ */
use MusicBrainz\MusicBrainz; use MusicBrainz\MusicBrainz;
use MusicBrainz\Clients\RequestsMbClient; use MusicBrainz\HttpAdapters\RequestsHttpAdapter;
/** /**
* Art Class * Art Class
@ -1055,7 +1055,7 @@ class Art extends database_object
return $images; return $images;
} }
$mb = new MusicBrainz(new RequestsMbClient()); $mb = new MusicBrainz(new RequestsHttpAdapter());
$includes = array( $includes = array(
'url-rels' 'url-rels'
); );
@ -1065,7 +1065,7 @@ class Art extends database_object
return $images; return $images;
} }
$asin = $release['asin']; $asin = $release->asin;
if ($asin) { if ($asin) {
debug_event('mbz-gatherart', "Found ASIN: " . $asin, '5'); debug_event('mbz-gatherart', "Found ASIN: " . $asin, '5');

View file

@ -243,12 +243,27 @@ class AutoUpdate
*/ */
public static function update_files() public static function update_files()
{ {
echo T_('Updating Ampache sources with `git pull` ...') . '<br />'; $cmd = 'git pull https://github.com/ampache/ampache.git';
echo T_('Updating Ampache sources with `' . $cmd . '` ...') . '<br />';
ob_flush(); ob_flush();
chdir(AmpConfig::get('prefix')); chdir(AmpConfig::get('prefix'));
exec('git pull https://github.com/ampache/ampache.git'); exec($cmd);
echo T_('Done') . '<br />'; echo T_('Done') . '<br />';
ob_flush(); ob_flush();
self::get_latest_version(true); self::get_latest_version(true);
} }
/**
* Update project dependencies.
*/
public static function update_dependencies()
{
$cmd = 'composer install --prefer-source --no-interaction';
echo T_('Updating dependencies with `' . $cmd . '` ...') . '<br />';
ob_flush();
chdir(AmpConfig::get('prefix'));
exec($cmd);
echo T_('Done') . '<br />';
ob_flush();
}
} }

View file

@ -221,7 +221,7 @@ abstract class Catalog extends database_object
/** /**
* create_catalog_type * create_catalog_type
* This function attempts to create a catalog type * This function attempts to create a catalog type
* all Catalog modules should be located in /modules/catalog/<name>.class.php * all Catalog modules should be located in /modules/catalog/<name>/<name>.class.php
* @param string $type * @param string $type
* @param int $id * @param int $id
* @return Catalog|null * @return Catalog|null
@ -232,7 +232,7 @@ abstract class Catalog extends database_object
return false; return false;
} }
$filename = AmpConfig::get('prefix') . '/modules/catalog/' . $type . '.catalog.php'; $filename = AmpConfig::get('prefix') . '/modules/catalog/' . $type . '/' . $type . '.catalog.php';
$include = require_once $filename; $include = require_once $filename;
if (!$include) { if (!$include) {
@ -313,7 +313,8 @@ abstract class Catalog extends database_object
public static function get_catalog_types() public static function get_catalog_types()
{ {
/* First open the dir */ /* First open the dir */
$handle = opendir(AmpConfig::get('prefix') . '/modules/catalog'); $basedir = AmpConfig::get('prefix') . '/modules/catalog';
$handle = opendir($basedir);
if (!is_resource($handle)) { if (!is_resource($handle)) {
debug_event('catalog', 'Error: Unable to read catalog types directory', '1'); debug_event('catalog', 'Error: Unable to read catalog types directory', '1');
@ -323,16 +324,22 @@ abstract class Catalog extends database_object
$results = array(); $results = array();
while (false !== ($file = readdir($handle))) { while (false !== ($file = readdir($handle))) {
if (substr($file, -11, 11) != 'catalog.php') { if ($file === '.' || $file === '..') {
continue; continue;
} }
/* Make sure it is a dir */
/* Make sure it isn't a dir */ if (! is_dir($basedir . '/' . $file)) {
if (!is_dir($file)) { debug_event('catalog', $file . ' is not a directory.', 3);
/* Get the basename and then everything before catalog */ continue;
$filename = basename($file, '.catalog.php');
$results[] = $filename;
} }
// Make sure the plugin base file exists inside the plugin directory
if (! file_exists($basedir . '/' . $file . '/' . $file . '.catalog.php')) {
debug_event('catalog', 'Missing class for ' . $file, 3);
continue;
}
$results[] = $file;
} // end while } // end while
return $results; return $results;
@ -613,7 +620,7 @@ abstract class Catalog extends database_object
} }
$insert_id = 0; $insert_id = 0;
$filename = AmpConfig::get('prefix') . '/modules/catalog/' . $type . '.catalog.php'; $filename = AmpConfig::get('prefix') . '/modules/catalog/' . $type . '/' . $type . '.catalog.php';
$include = require_once $filename; $include = require_once $filename;
if ($include) { if ($include) {
@ -710,6 +717,11 @@ abstract class Catalog extends database_object
$db_results = Dba::read($sql, $params); $db_results = Dba::read($sql, $params);
$data = Dba::fetch_row($db_results); $data = Dba::fetch_row($db_results);
$playlists = $data[0]; $playlists = $data[0];
$sql = 'SELECT COUNT(`id`) FROM `live_stream`';
$db_results = Dba::read($sql, $params);
$data = Dba::fetch_row($db_results);
$live_streams = $data[0];
$results = array(); $results = array();
$results['songs'] = $songs; $results['songs'] = $songs;
@ -718,6 +730,7 @@ abstract class Catalog extends database_object
$results['artists'] = $artists; $results['artists'] = $artists;
$results['playlists'] = $playlists; $results['playlists'] = $playlists;
$results['smartplaylists'] = $smartplaylists; $results['smartplaylists'] = $smartplaylists;
$results['live_streams'] = $live_streams;
$results['size'] = $size; $results['size'] = $size;
$results['time'] = $time; $results['time'] = $time;
@ -943,7 +956,8 @@ abstract class Catalog extends database_object
$sql_limit = "LIMIT " . $offset . ", 18446744073709551615"; $sql_limit = "LIMIT " . $offset . ", 18446744073709551615";
} }
$sql = "SELECT `artist`.id, `artist`.`name`, `artist`.`summary` FROM `song` LEFT JOIN `artist` ON `artist`.`id` = `song`.`artist` " . $sql = "SELECT `artist`.id, `artist`.`name`, `artist`.`summary`, (SELECT COUNT(DISTINCT album) from `song` as `inner_song` WHERE `inner_song`.`artist` = `song`.`artist`) AS `albums`" .
"FROM `song` LEFT JOIN `artist` ON `artist`.`id` = `song`.`artist` " .
$sql_where . $sql_where .
"GROUP BY `song`.artist ORDER BY `artist`.`name` " . "GROUP BY `song`.artist ORDER BY `artist`.`name` " .
$sql_limit; $sql_limit;
@ -1555,7 +1569,7 @@ abstract class Catalog extends database_object
return $info; return $info;
} // update_song_from_tags } // update_song_from_tags
public function update_video_from_tags($results, Video $video) public static function update_video_from_tags($results, Video $video)
{ {
// TODO: implement this // TODO: implement this
return null; return null;
@ -2093,6 +2107,114 @@ abstract class Catalog extends database_object
return (Access::check('interface','75') || ($libitem->get_user_owner() == $user && AmpConfig::get('upload_allow_remove'))); return (Access::check('interface','75') || ($libitem->get_user_owner() == $user && AmpConfig::get('upload_allow_remove')));
} }
public static function process_action($action, $catalogs, $options = null)
{
if (!$options || !is_array($options)) {
$options = array();
}
switch ($action) {
case 'add_to_all_catalogs':
$catalogs = Catalog::get_catalogs();
case 'add_to_catalog':
if ($catalogs) {
foreach ($catalogs as $catalog_id) {
$catalog = Catalog::create_from_id($catalog_id);
if ($catalog !== null) {
$catalog->add_to_catalog($options);
}
}
if (!defined('SSE_OUTPUT')) {
Error::display('catalog_add');
}
}
break;
case 'update_all_catalogs':
$catalogs = Catalog::get_catalogs();
case 'update_catalog':
if ($catalogs) {
foreach ($catalogs as $catalog_id) {
$catalog = Catalog::create_from_id($catalog_id);
if ($catalog !== null) {
$catalog->verify_catalog();
}
}
}
break;
case 'full_service':
if (!$catalogs) {
$catalogs = Catalog::get_catalogs();
}
/* This runs the clean/verify/add in that order */
foreach ($catalogs as $catalog_id) {
$catalog = Catalog::create_from_id($catalog_id);
if ($catalog !== null) {
$catalog->clean_catalog();
$catalog->verify_catalog();
$catalog->add_to_catalog();
}
}
Dba::optimize_tables();
break;
case 'clean_all_catalogs':
$catalogs = Catalog::get_catalogs();
case 'clean_catalog':
if ($catalogs) {
foreach ($catalogs as $catalog_id) {
$catalog = Catalog::create_from_id($catalog_id);
if ($catalog !== null) {
$catalog->clean_catalog();
}
} // end foreach catalogs
Dba::optimize_tables();
}
break;
case 'update_from':
$catalog_id = 0;
// First see if we need to do an add
if ($options['add_path'] != '/' && strlen($options['add_path'])) {
if ($catalog_id = Catalog_local::get_from_path($options['add_path'])) {
$catalog = Catalog::create_from_id($catalog_id);
if ($catalog !== null) {
$catalog->add_to_catalog(array('subdirectory'=>$options['add_path']));
}
}
} // end if add
// Now check for an update
if ($options['update_path'] != '/' && strlen($options['update_path'])) {
if ($catalog_id = Catalog_local::get_from_path($options['update_path'])) {
$songs = Song::get_from_path($options['update_path']);
foreach ($songs as $song_id) {
Catalog::update_single_item('song',$song_id);
}
}
} // end if update
if ($catalog_id <= 0) {
Error::add('general', T_("This subdirectory is not part of an existing catalog. Update cannot be processed."));
}
break;
case 'gather_media_art':
if (!$catalogs) {
$catalogs = Catalog::get_catalogs();
}
// Iterate throught the catalogs and gather as needed
foreach ($catalogs as $catalog_id) {
$catalog = Catalog::create_from_id($catalog_id);
if ($catalog !== null) {
require AmpConfig::get('prefix') . UI::find_template('show_gather_art.inc.php');
flush();
$catalog->gather_art();
}
}
break;
}
}
} }
// end of catalog class // end of catalog class

View file

@ -24,13 +24,12 @@ class Graph
{ {
public function __construct() public function __construct()
{ {
require_once AmpConfig::get('prefix') . '/modules/pChart/pData.class.php'; require_once AmpConfig::get('prefix') . '/lib/vendor/szymach/c-pchart/src/Classes/pData.php';
require_once AmpConfig::get('prefix') . '/modules/pChart/pDraw.class.php'; require_once AmpConfig::get('prefix') . '/lib/vendor/szymach/c-pchart/src/Classes/pDraw.php';
require_once AmpConfig::get('prefix') . '/modules/pChart/pImage.class.php'; require_once AmpConfig::get('prefix') . '/lib/vendor/szymach/c-pchart/src/Classes/pImage.php';
return true; return true;
} }
protected function get_sql_date_format($field, $zoom) protected function get_sql_date_format($field, $zoom)
{ {
switch ($zoom) { switch ($zoom) {
@ -132,7 +131,7 @@ class Graph
return $values; return $values;
} }
protected function get_all_pts($fct, pData $MyData, $id = 0, $object_type = null, $object_id = 0, $start_date = null, $end_date = null, $zoom = 'day', $show_total = true) protected function get_all_pts($fct, CpChart\Classes\pData $MyData, $id = 0, $object_type = null, $object_id = 0, $start_date = null, $end_date = null, $zoom = 'day', $show_total = true)
{ {
$values = $this->get_all_type_pts($fct, $id, $object_type, $object_id, $start_date, $end_date, $zoom); $values = $this->get_all_type_pts($fct, $id, $object_type, $object_id, $start_date, $end_date, $zoom);
foreach ($values as $date => $value) { foreach ($values as $date => $value) {
@ -144,7 +143,7 @@ class Graph
return $values; return $values;
} }
protected function get_user_all_pts($fct, pData $MyData, $user = 0, $object_type = null, $object_id = 0, $start_date = null, $end_date = null, $zoom = 'day') protected function get_user_all_pts($fct, CpChart\Classes\pData $MyData, $user = 0, $object_type = null, $object_id = 0, $start_date = null, $end_date = null, $zoom = 'day')
{ {
$values = $this->get_all_pts($fct, $MyData, $user, $object_type, $object_id, $start_date, $end_date, $zoom); $values = $this->get_all_pts($fct, $MyData, $user, $object_type, $object_id, $start_date, $end_date, $zoom);
@ -167,7 +166,7 @@ class Graph
} }
} }
protected function get_catalog_all_pts($fct, pData $MyData, $catalog = 0, $object_type = null, $object_id = 0, $start_date = null, $end_date = null, $zoom = 'day') protected function get_catalog_all_pts($fct, CpChart\Classes\pData $MyData, $catalog = 0, $object_type = null, $object_id = 0, $start_date = null, $end_date = null, $zoom = 'day')
{ {
$values = $this->get_all_pts($fct, $MyData, $catalog, $object_type, $object_id, $start_date, $end_date, $zoom, false); $values = $this->get_all_pts($fct, $MyData, $catalog, $object_type, $object_id, $start_date, $end_date, $zoom, false);
@ -289,7 +288,7 @@ class Graph
return $pts; return $pts;
} }
protected function render_graph($title, pData $MyData, $zoom, $width = 0, $height = 0) protected function render_graph($title, CpChart\Classes\pData $MyData, $zoom, $width = 0, $height = 0)
{ {
// Check graph size sanity // Check graph size sanity
$width = intval($width); $width = intval($width);
@ -319,7 +318,7 @@ class Graph
} }
/* Create the pChart object */ /* Create the pChart object */
$myPicture = new pImage($width,$height,$MyData); $myPicture = new CpChart\Classes\pImage($width,$height,$MyData);
/* Turn of Antialiasing */ /* Turn of Antialiasing */
$myPicture->Antialias = FALSE; $myPicture->Antialias = FALSE;
@ -336,12 +335,13 @@ class Graph
/* Add a border to the picture */ /* Add a border to the picture */
$myPicture->drawRectangle(0,0,$width-1,$height-1,array("R"=>0,"G"=>0,"B"=>0)); $myPicture->drawRectangle(0,0,$width-1,$height-1,array("R"=>0,"G"=>0,"B"=>0));
$font_path = AmpConfig::get('prefix')."/lib/vendor/szymach/c-pchart/src/Resources/fonts";
/* Write the chart title */ /* Write the chart title */
$myPicture->setFontProperties(array("FontName"=>AmpConfig::get('prefix')."/modules/pChart/fonts/Forgotte.ttf","FontSize"=>11)); $myPicture->setFontProperties(array("FontName"=>$font_path."/Forgotte.ttf","FontSize"=>11));
$myPicture->drawText(150,35,$title,array("FontSize"=>20,"Align"=>TEXT_ALIGN_BOTTOMMIDDLE)); $myPicture->drawText(150,35,$title,array("FontSize"=>20,"Align"=>TEXT_ALIGN_BOTTOMMIDDLE));
/* Set the default font */ /* Set the default font */
$myPicture->setFontProperties(array("FontName"=>AmpConfig::get('prefix')."/modules/pChart/fonts/pf_arma_five.ttf","FontSize"=>6)); $myPicture->setFontProperties(array("FontName"=>$font_path."/pf_arma_five.ttf","FontSize"=>6));
/* Define the chart area */ /* Define the chart area */
$myPicture->setGraphArea(60,40,$width-20,$height-50); $myPicture->setGraphArea(60,40,$width-20,$height-50);
@ -370,7 +370,7 @@ class Graph
public function render_user_hits($user = 0, $object_type, $object_id, $start_date = null, $end_date = null, $zoom = 'day', $width = 0, $height = 0) public function render_user_hits($user = 0, $object_type, $object_id, $start_date = null, $end_date = null, $zoom = 'day', $width = 0, $height = 0)
{ {
$MyData = new pData(); $MyData = new CpChart\Classes\pData();
$this->get_user_all_pts('get_user_hits_pts', $MyData, $user, $object_type, $object_id, $start_date, $end_date, $zoom); $this->get_user_all_pts('get_user_hits_pts', $MyData, $user, $object_type, $object_id, $start_date, $end_date, $zoom);
$MyData->setAxisName(0, "Hits"); $MyData->setAxisName(0, "Hits");
@ -381,7 +381,7 @@ class Graph
public function render_user_bandwidth($user = 0, $object_type = null, $object_id = 0, $start_date = null, $end_date = null, $zoom = 'day', $width = 0, $height = 0) public function render_user_bandwidth($user = 0, $object_type = null, $object_id = 0, $start_date = null, $end_date = null, $zoom = 'day', $width = 0, $height = 0)
{ {
$MyData = new pData(); $MyData = new CpChart\Classes\pData();
$this->get_user_all_pts('get_user_bandwidth_pts', $MyData, $user, $object_type, $object_id, $start_date, $end_date, $zoom); $this->get_user_all_pts('get_user_bandwidth_pts', $MyData, $user, $object_type, $object_id, $start_date, $end_date, $zoom);
$MyData->setAxisName(0, "Bandwidth"); $MyData->setAxisName(0, "Bandwidth");
@ -425,7 +425,7 @@ class Graph
public function render_catalog_files($catalog = 0, $object_type = null, $object_id = 0, $start_date = null, $end_date = null, $zoom = 'day', $width = 0, $height = 0) public function render_catalog_files($catalog = 0, $object_type = null, $object_id = 0, $start_date = null, $end_date = null, $zoom = 'day', $width = 0, $height = 0)
{ {
$MyData = new pData(); $MyData = new CpChart\Classes\pData();
$this->get_catalog_all_pts('get_catalog_files_pts', $MyData, $catalog, $object_type, $object_id, $start_date, $end_date, $zoom); $this->get_catalog_all_pts('get_catalog_files_pts', $MyData, $catalog, $object_type, $object_id, $start_date, $end_date, $zoom);
$MyData->setAxisName(0, "Files"); $MyData->setAxisName(0, "Files");
@ -436,7 +436,7 @@ class Graph
public function render_catalog_size($catalog = 0, $object_type = null, $object_id = 0, $start_date = null, $end_date = null, $zoom = 'day', $width = 0, $height = 0) public function render_catalog_size($catalog = 0, $object_type = null, $object_id = 0, $start_date = null, $end_date = null, $zoom = 'day', $width = 0, $height = 0)
{ {
$MyData = new pData(); $MyData = new CpChart\Classes\pData();
$this->get_catalog_all_pts('get_catalog_size_pts', $MyData, $catalog, $object_type, $object_id, $start_date, $end_date, $zoom); $this->get_catalog_all_pts('get_catalog_size_pts', $MyData, $catalog, $object_type, $object_id, $start_date, $end_date, $zoom);
$MyData->setAxisName(0, "Size"); $MyData->setAxisName(0, "Size");

View file

@ -192,6 +192,13 @@ class Live_Stream extends database_object implements media, library_item
if (!in_array($elements['0'],$allowed_array)) { if (!in_array($elements['0'],$allowed_array)) {
Error::add('general', T_('Invalid URL must be mms:// , https:// or http://')); Error::add('general', T_('Invalid URL must be mms:// , https:// or http://'));
} }
if (!empty($data['site_url'])) {
$elements = explode(":", $data['site_url']);
if (!in_array($elements['0'], $allowed_array)) {
Error::add('site_url', T_('Invalid URL must be http:// or https://'));
}
}
if (Error::occurred()) { if (Error::occurred()) {
return false; return false;
@ -219,9 +226,16 @@ class Live_Stream extends database_object implements media, library_item
$elements = explode(":", $data['url']); $elements = explode(":", $data['url']);
if (!in_array($elements['0'],$allowed_array)) { if (!in_array($elements['0'], $allowed_array)) {
Error::add('url', T_('Invalid URL must be http:// or https://')); Error::add('url', T_('Invalid URL must be http:// or https://'));
} }
if (!empty($data['site_url'])) {
$elements = explode(":", $data['site_url']);
if (!in_array($elements['0'], $allowed_array)) {
Error::add('site_url', T_('Invalid URL must be http:// or https://'));
}
}
// Make sure it's a real catalog // Make sure it's a real catalog
$catalog = Catalog::create_from_id($data['catalog']); $catalog = Catalog::create_from_id($data['catalog']);

View file

@ -99,7 +99,7 @@ class Localplay
return false; return false;
} }
$filename = AmpConfig::get('prefix') . '/modules/localplay/' . $this->type . '.controller.php'; $filename = AmpConfig::get('prefix') . '/modules/localplay/' . $this->type . '/' . $this->type . '.controller.php';
$include = require_once $filename; $include = require_once $filename;
if (!$include) { if (!$include) {
@ -138,7 +138,8 @@ class Localplay
public static function get_controllers() public static function get_controllers()
{ {
/* First open the dir */ /* First open the dir */
$handle = opendir(AmpConfig::get('prefix') . '/modules/localplay'); $basedir = AmpConfig::get('prefix') . '/modules/localplay';
$handle = opendir($basedir);
if (!is_resource($handle)) { if (!is_resource($handle)) {
debug_event('Localplay','Error: Unable to read localplay controller directory','1'); debug_event('Localplay','Error: Unable to read localplay controller directory','1');
@ -148,16 +149,22 @@ class Localplay
$results = array(); $results = array();
while (false !== ($file = readdir($handle))) { while (false !== ($file = readdir($handle))) {
if (substr($file,-14,14) != 'controller.php') { if ($file === '.' || $file === '..') {
continue; continue;
} }
/* Make sure it is a dir */
/* Make sure it isn't a dir */ if (! is_dir($basedir . '/' . $file)) {
if (!is_dir($file)) { debug_event('Localplay', $file . ' is not a directory.', 3);
/* Get the basename and then everything before controller */ continue;
$filename = basename($file,'.controller.php');
$results[] = $filename;
} }
// Make sure the plugin base file exists inside the plugin directory
if (! file_exists($basedir . '/' . $file . '/' . $file . '.controller.php')) {
debug_event('Localplay', 'Missing class for ' . $file, 3);
continue;
}
$results[] = $file;
} // end while } // end while
return $results; return $results;

View file

@ -93,7 +93,7 @@ class Movie extends Video
$prefix = $this->prefix; $prefix = $this->prefix;
} }
$summary = isset($data['summary']) ? $data['summary'] : $this->summary; $summary = isset($data['summary']) ? $data['summary'] : $this->summary;
$year = isset($data['year']) ? $data['summary'] : $this->year; $year = isset($data['year']) ? $data['year'] : $this->year;
$sql = "UPDATE `movie` SET `original_name` = ?, `prefix` = ?, `summary` = ?, `year` = ? WHERE `id` = ?"; $sql = "UPDATE `movie` SET `original_name` = ?, `prefix` = ?, `summary` = ?, `year` = ? WHERE `id` = ?";
Dba::write($sql, array($name, $prefix, $summary, $year, $this->id)); Dba::write($sql, array($name, $prefix, $summary, $year, $this->id));

View file

@ -51,14 +51,21 @@ class Plugin
*/ */
public function _get_info($name) public function _get_info($name)
{ {
/* Require the file we want */ try {
require_once AmpConfig::get('prefix') . '/modules/plugins/' . $name . '.plugin.php'; /* Require the file we want */
if (!@include_once(AmpConfig::get('prefix') . '/modules/plugins/' . $name . '/' . $name . '.plugin.php')) {
debug_event('plugin', 'Cannot include plugin `' . $name . '`.', 1);
return false;
}
$plugin_name = "Ampache$name"; $plugin_name = "Ampache$name";
$this->_plugin = new $plugin_name();
$this->_plugin = new $plugin_name(); if (!$this->is_valid()) {
return false;
if (!$this->is_valid()) { }
} catch (Exception $ex) {
debug_event('plugin', 'Error when initializing plugin `' . $name . '`: ' . $ex->getMessage(), 1);
return false; return false;
} }
@ -80,7 +87,8 @@ class Plugin
$plugins_list[$type] = array(); $plugins_list[$type] = array();
// Open up the plugin dir // Open up the plugin dir
$handle = opendir(AmpConfig::get('prefix') . '/modules/plugins'); $basedir = AmpConfig::get('prefix') . '/modules/plugins';
$handle = opendir($basedir);
if (!is_resource($handle)) { if (!is_resource($handle)) {
debug_event('Plugins','Unable to read plugins directory','1'); debug_event('Plugins','Unable to read plugins directory','1');
@ -88,31 +96,38 @@ class Plugin
// Recurse the directory // Recurse the directory
while (false !== ($file = readdir($handle))) { while (false !== ($file = readdir($handle))) {
// Ignore non-plugin files if ($file === '.' || $file === '..') {
if (substr($file,-10,10) != 'plugin.php') {
continue; continue;
} }
if (is_dir($file)) { // Take care of directories only
if (!is_dir($basedir . '/' . $file)) {
debug_event('Plugins', $file . ' is not a directory.', 3);
continue; continue;
} }
$plugin_name = basename($file,'.plugin.php');
// Make sure the plugin base file exists inside the plugin directory
if (! file_exists($basedir . '/' . $file . '/' . $file . '.plugin.php')) {
debug_event('Plugins', 'Missing class for ' . $file, 3);
continue;
}
if ($type != '') { if ($type != '') {
$plugin = new Plugin($plugin_name); $plugin = new Plugin($file);
if (! Plugin::is_installed($plugin->_plugin->name)) { if (! Plugin::is_installed($plugin->_plugin->name)) {
debug_event('Plugins', 'Plugin ' . $plugin->_plugin->name . ' is not installed, skipping', 6); debug_event('Plugins', 'Plugin ' . $plugin->_plugin->name . ' is not installed, skipping', 6);
continue; continue;
} }
if (! $plugin->is_valid()) { if (! $plugin->is_valid()) {
debug_event('Plugins', 'Plugin ' . $plugin_name . ' is not valid, skipping', 6); debug_event('Plugins', 'Plugin ' . $file . ' is not valid, skipping', 6);
continue; continue;
} }
if (! method_exists($plugin->_plugin, $type)) { if (! method_exists($plugin->_plugin, $type)) {
debug_event('Plugins', 'Plugin ' . $plugin_name . ' does not support ' . $type . ', skipping', 6); debug_event('Plugins', 'Plugin ' . $file . ' does not support ' . $type . ', skipping', 6);
continue; continue;
} }
} }
// It's a plugin record it // It's a plugin record it
$plugins_list[$type][$plugin_name] = $plugin_name; $plugins_list[$type][$file] = $file;
} // end while } // end while
// Little stupid but hey // Little stupid but hey

View file

@ -23,35 +23,90 @@
class scrobbler class scrobbler
{ {
public $error_msg; public $error_msg;
public $username;
public $password;
public $challenge; public $challenge;
public $submit_host; public $host;
public $submit_port; public $scheme;
public $submit_url; public $api_key;
public $queued_tracks; public $queued_tracks;
public $reset_handshake = false; private $secret;
public $scrobble_host = 'post.audioscrobbler.com';
/** /**
* Constructor * Constructor
* This is the constructer it takes a username and password * This is the constructer it takes a username and password
*/ */
public function __construct($username, $password,$host='',$port='',$url='',$challenge='',$scrobble_host='') public function __construct($api_key, $scheme='https',$host='',$challenge='', $secret='')
{ {
$this->error_msg = ''; $this->error_msg = '';
$this->username = trim($username);
$this->password = trim($password);
$this->challenge = $challenge; $this->challenge = $challenge;
$this->submit_host = $host; $this->host = $host;
$this->submit_port = $port; $this->scheme = $scheme;
$this->submit_url = $url; $this->api_key = $api_key;
$this->secret=$secret;
$this->queued_tracks = array(); $this->queued_tracks = array();
if ($scrobble_host) {
$this->scrobble_host = $scrobble_host;
}
} // scrobbler } // scrobbler
/**
* get_api_sig
* Provide the API signature for calling Last.fm / Libre.fm services
* It is the md5 of the <name><value> of all parameter plus API's secret
*/
public function get_api_sig($vars=null)
{
ksort($vars);
$sig = '';
foreach ($vars as $name => $value) {
$sig .= $name.$value;
}
$sig .= $this->secret;
$sig = md5($sig);
return $sig;
} // get_api_sig
/**
* call_url
* This is a generic caller for HTTP requests
* It need the method (GET/POST), the url and the parameters
*/
public function call_url($url, $method='GET', $vars=null)
{
// Encode parameters per RFC1738
$params=http_build_query($vars);
$opts = array(
'http'=>array(
'method'=>$method,
'header'=> array(
'Host: '.$this->host,
'User-Agent: Ampache/'.AmpConfig::get('version')
),
)
);
// POST request need parameters in body and additional headers
if ($method == 'POST') {
$opts['http']['content'] = $params;
$opts['http']['header'][] = 'Content-type: application/x-www-form-urlencoded';
$opts['http']['header'][] = 'Content-length: '.strlen($params);
$params='';
}
$context = stream_context_create($opts);
if ($params!='') {
// If there are paramters for GET request, adding the "?" caracter before
$params='?'.$params;
}
//debug_event('SCROBBLER', "$this->scheme://$this->host$url$params", 5);
//debug_event('SCROBBLER', serialize($opts), 5);
$fp = fopen("$this->scheme://$this->host$url$params", 'r', false, $context);
if (!$fp) {
return false;
}
ob_start();
fpassthru($fp);
$buffer = ob_get_contents();
ob_end_clean();
fclose($fp);
//debug_event('SCROBBLER', $buffer, 5);
return $buffer;
} // call_url
/** /**
* get_error_msg * get_error_msg
*/ */
@ -69,83 +124,50 @@ class scrobbler
} // get_queue_count } // get_queue_count
/** /**
* handshake * get_session_key
* This does a handshake with the audioscrobber server it doesn't pass the password, but * This is a generic caller for HTTP requests
* it does pass the username and has a 10 second timeout * It need the method (GET/POST), the url and the parameters
*/ */
public function handshake() public function get_session_key ($token=null)
{ {
$data = array(); if (!is_null($token)) {
$as_socket = fsockopen($this->scrobble_host, 80, $errno, $errstr, 2); $vars = array(
if (!$as_socket) { 'method' => 'auth.getSession',
$this->error_msg = $errstr; 'api_key'=> $this->api_key,
return false; 'token' => $token
);
//sign the call
$sig = $this->get_api_sig($vars);
$vars['api_sig'] = $sig;
//call the getSession API
$response=$this->call_url('/2.0/', 'GET', $vars);
$xml = simplexml_load_string($response);
if ($xml) {
$status = (string) $xml['status'];
if ($status == 'ok') {
if ($xml->session && $xml->session->key) {
return $xml->session->key;
} else {
$this->error_msg = 'Did not receive a valid response';
return false;
}
} else {
$this->error_msg = $xml->error;
return false;
}
} else {
$this->error_msg = 'Did not receive a valid response';
return false;
}
} }
$this->error_msg = 'Need a token to call getSession';
$username = rawurlencode($this->username); return false;
$timestamp = time(); } // get_session_key
$auth_token = rawurlencode(md5($this->password . $timestamp));
$get_string = "GET /?hs=true&p=1.2&c=apa&v=0.1&u=$username&t=$timestamp&a=$auth_token HTTP/1.1\r\n";
fwrite($as_socket, $get_string);
fwrite($as_socket, "Host: $this->scrobble_host\r\n");
fwrite($as_socket, "Accept: */*\r\n\r\n");
$buffer = '';
while (!feof($as_socket)) {
$buffer .= fread($as_socket, 4096);
}
fclose($as_socket);
$split_response = preg_split("/\r\n\r\n/", $buffer);
if (!isset($split_response[1])) {
$this->error_msg = 'Did not receive a valid response';
return false;
}
$response = explode("\n", $split_response[1]);
// Handle the fact Libre.FM has extranious values at the start of it's handshake response
if (is_numeric(trim($response['0']))) {
array_shift($response);
debug_event('SCROBBLER','Junk in handshake, removing first line',1);
}
if (substr($response[0], 0, 6) == 'FAILED') {
$this->error_msg = substr($response[0], 7);
return false;
}
if (substr($response[0], 0, 7) == 'BADUSER') {
$this->error_msg = 'Invalid Username';
return false;
}
if (substr($response[0],0,7) == 'BADTIME') {
$this->error_msg = 'Your time is too far off from the server, or your PHP timezone is incorrect';
return false;
}
if (substr($response[0], 0, 6) == 'UPDATE') {
$this->error_msg = 'You need to update your client: '.substr($response[0], 7);
return false;
}
if (preg_match('/http:\/\/([^\/]+)\/(.*)$/', $response[3], $matches)) {
$host_parts = explode(":",$matches[1]);
$data['submit_host'] = $host_parts[0];
$data['submit_port'] = $host_parts[1] ? $host_parts[1] : '80';
$data['submit_url'] = '/' . $matches[2];
} else {
$this->error_msg = "Invalid POST URL returned, unable to continue. Sent:\n$get_string\n----\nReceived:\n" . $buffer .
"\n---------\nExpected:" . print_r($response, true);
return false;
}
// Remove any extra junk around the challenge
$data['challenge'] = trim($response[1]);
return $data;
} // handshake
/** /**
* queue_track * queue_track
* This queues the LastFM track by storing it in this object, it doesn't actually * This queues the LastFM / Libre.fm track by storing it in this object, it doesn't actually
* submit the track or talk to LastFM in anyway, kind of useless for our uses but its * submit the track or talk to LastFM / Libre in anyway, kind of useless for our uses but its
* here, and that's how it is. * here, and that's how it is.
*/ */
public function queue_track($artist, $album, $title, $timestamp, $length,$track) public function queue_track($artist, $album, $title, $timestamp, $length,$track)
@ -169,8 +191,8 @@ class scrobbler
/** /**
* submit_tracks * submit_tracks
* This actually talks to LastFM submiting the tracks that are queued up. It * This actually talks to LastFM / Libre.fm submiting the tracks that are queued up.
* passed the md5'd password combinted with the challenge, which is then md5'd * It passed the API key, session key combinted with the signature
*/ */
public function submit_tracks() public function submit_tracks()
{ {
@ -183,84 +205,78 @@ class scrobbler
//sort array by timestamp //sort array by timestamp
ksort($this->queued_tracks); ksort($this->queued_tracks);
// build the query string // Build the query string (encoded per RFC1738 by the call method)
$query_str = 's='.rawurlencode($this->challenge).'&';
$i = 0; $i = 0;
$vars= array();
foreach ($this->queued_tracks as $track) { foreach ($this->queued_tracks as $track) {
$query_str .= "a[$i]=".rawurlencode($track['artist'])."&t[$i]=".rawurlencode($track['title'])."&b[$i]=".rawurlencode($track['album'])."&"; //construct array of parameters for each song
$query_str .= "m[$i]=&l[$i]=".rawurlencode($track['length'])."&i[$i]=".rawurlencode($track['time'])."&"; $vars["artist[$i]"] = $track['artist'];
$query_str .= "n[$i]=" . rawurlencode($track['track']) . "&o[$i]=P&r[$i]=&"; $vars["track[$i]"] = $track['title'];
$vars["timestamp[$i]"] = $track['time'];
$vars["album[$i]"] = $track['album'];
$vars["trackNumber[$i]"] = $track['track'];
$vars["duration[$i]"] = $track['length'];
$i++; $i++;
} }
// Add the method, API and session keys
$vars['method'] = 'track.scrobble';
$vars['api_key'] = $this->api_key;
$vars['sk'] = $this->challenge;
if (!trim($this->submit_host) || !$this->submit_port) { // Sign the call
$this->reset_handshake = true; $sig = $this->get_api_sig($vars);
return false; $vars['api_sig'] = $sig;
}
$as_socket = fsockopen($this->submit_host, intval($this->submit_port), $errno, $errstr, 2); // Call the method and parse response
$response=$this->call_url('/2.0/', 'POST', $vars);
if (!$as_socket) { $xml = simplexml_load_string($response);
$this->error_msg = $errstr; if ($xml) {
$this->reset_handshake = true; $status = (string) $xml['status'];
return false; if ($status == 'ok') {
} return true;
} else {
$action = "POST ".$this->submit_url." HTTP/1.0\r\n"; $this->error_msg = $xml->error;
fwrite($as_socket, $action); return false;
fwrite($as_socket, "Host: ".$this->submit_host."\r\n"); }
fwrite($as_socket, "Accept: */*\r\n"); } else {
fwrite($as_socket, "User-Agent: Ampache/3.6\r\n");
fwrite($as_socket, "Content-type: application/x-www-form-urlencoded\r\n");
fwrite($as_socket, "Content-length: ".strlen($query_str)."\r\n\r\n");
fwrite($as_socket, $query_str."\r\n\r\n");
// Allow us to debug this
debug_event('SCROBBLER','Query String:' . $query_str,6);
$buffer = '';
while (!feof($as_socket)) {
$buffer .= fread($as_socket, 8192);
}
fclose($as_socket);
$split_response = preg_split("/\r\n\r\n/", $buffer);
if (!isset($split_response[1])) {
$this->error_msg = 'Did not receive a valid response'; $this->error_msg = 'Did not receive a valid response';
$this->reset_handshake = true;
return false; return false;
} }
$response = explode("\n", $split_response[1]);
if (!isset($response[0])) {
$this->error_msg = 'Unknown error submitting tracks'.
"\nDebug output:\n".$buffer;
$this->reset_handshake = true;
return false;
}
if (substr($response[0], 0, 6) == 'FAILED') {
$this->error_msg = $response[0];
$this->reset_handshake = true;
return false;
}
if (substr($response[0], 0, 7) == 'BADAUTH') {
$this->error_msg = 'Invalid username/password (' . trim($response[0]) . ')';
return false;
}
if (substr($response[0],0,10) == 'BADSESSION') {
$this->error_msg = 'Invalid Session passed (' . trim($response[0]) . ')';
$this->reset_handshake = true;
return false;
}
if (substr($response[0], 0, 2) != 'OK') {
$this->error_msg = 'Response Not ok, unknown error'.
"\nDebug output:\n".$buffer;
$this->reset_handshake = true;
return false;
}
return true;
} // submit_tracks } // submit_tracks
/**
* love
* This takes care of spreading your love to the world
* It passed the API key, session key combinted with the signature
*/
public function love($is_loved, $type, $artist = '', $title = '', $album = '')
{
$vars['track'] = $title;
$vars['artist'] = $artist;
// Add the method, API and session keys
$vars['method'] = $is_loved ? 'track.love' : 'track.unlove';
$vars['api_key'] = $this->api_key;
$vars['sk'] = $this->challenge;
// Sign the call
$sig = $this->get_api_sig($vars);
$vars['api_sig'] = $sig;
// Call the method and parse response
$response=$this->call_url('/2.0/', 'POST', $vars);
$xml = simplexml_load_string($response);
if ($xml) {
$status = (string) $xml['status'];
if ($status == 'ok') {
return true;
} else {
$this->error_msg = $xml->error;
return false;
}
} else {
$this->error_msg = 'Did not receive a valid response';
return false;
}
} // love
} // end audioscrobbler class } // end audioscrobbler class

View file

@ -985,7 +985,7 @@ class Search extends playlist_object
$where[] = "COALESCE(`rating`.`rating`,0) $sql_match_operator '$input'"; $where[] = "COALESCE(`rating`.`rating`,0) $sql_match_operator '$input'";
} else { } else {
$group[] = "`album`.`id`"; $group[] = "`album`.`id`";
$having[] = "ROUND(AVG(`rating`.`rating`)) $sql_match_operator '$input'"; $having[] = "ROUND(AVG(IFNULL(`rating`.`rating`,0))) $sql_match_operator '$input'";
} }
$join['rating'] = true; $join['rating'] = true;
break; break;
@ -1246,7 +1246,7 @@ class Search extends playlist_object
$where[] = "COALESCE(`rating`.`rating`,0) $sql_match_operator '$input'"; $where[] = "COALESCE(`rating`.`rating`,0) $sql_match_operator '$input'";
} else { } else {
$group[] = "`song`.`id`"; $group[] = "`song`.`id`";
$having[] = "ROUND(AVG(`rating`.`rating`)) $sql_match_operator '$input'"; $having[] = "ROUND(AVG(IFNULL(`rating`.`rating`,0))) $sql_match_operator '$input'";
} }
$join['rating'] = true; $join['rating'] = true;
break; break;

View file

@ -164,6 +164,16 @@ class Shoutbox
} }
$object = new $type($object_id); $object = new $type($object_id);
if ($object->id > 0) {
if (strtolower($type) === 'song') {
if (!$object->enabled) {
$object = null;
}
}
} else {
$object = null;
}
return $object; return $object;
} // get_object } // get_object

View file

@ -841,7 +841,7 @@ class Song extends database_object implements media, library_item
unset($song->catalog,$song->played,$song->enabled,$song->addition_time,$song->update_time,$song->type); unset($song->catalog,$song->played,$song->enabled,$song->addition_time,$song->update_time,$song->type);
$array = array(); $array = array();
$string_array = array('title','comment','lyrics'); $string_array = array('title','comment','lyrics','composer','tags');
$skip_array = array('id','tag_id','mime','artist_mbid','album_mbid','albumartist_mbid','albumartist','mbid','mb_albumid_group','waveform','object_cnt'); $skip_array = array('id','tag_id','mime','artist_mbid','album_mbid','albumartist_mbid','albumartist','mbid','mb_albumid_group','waveform','object_cnt');
// Pull out all the currently set vars // Pull out all the currently set vars
@ -849,15 +849,33 @@ class Song extends database_object implements media, library_item
// Foreach them // Foreach them
foreach ($fields as $key=>$value) { foreach ($fields as $key=>$value) {
if (in_array($key,$skip_array)) { $key = trim($key);
if (empty($key) || in_array($key,$skip_array)) {
continue; continue;
} }
$songData = is_array($song->$key) ? implode(" ", $song->$key) : $song->$key; // Represent the value as a string for simpler comparaison.
$newSongData = is_array($new_song->$key) ? implode(" ", $new_song->$key) : $new_song->$key; // For array, ensure to sort similarly old/new values
if (is_array($song->$key)) {
$arr = $song->$key;
sort($arr);
$songData = implode(" ", $arr);
} else {
$songData = $song->$key;
}
if (is_array($new_song->$key)) {
$arr = $new_song->$key;
sort($arr);
$newSongData = implode(" ", $arr);
} else {
$newSongData = $new_song->$key;
}
// If it's a stringie thing // If it's a stringie thing
if (in_array($key, $string_array)) { if (in_array($key, $string_array)) {
if (trim(stripslashes($songData)) != trim(stripslashes($newSongData))) { $songData = self::clean_string_field_value($songData);
$newSongData = self::clean_string_field_value($newSongData);
if ($songData != $newSongData) {
$array['change'] = true; $array['change'] = true;
$array['element'][$key] = 'OLD: ' . $songData . ' --> ' . $newSongData; $array['element'][$key] = 'OLD: ' . $songData . ' --> ' . $newSongData;
} }
@ -877,6 +895,17 @@ class Song extends database_object implements media, library_item
return $array; return $array;
} // compare_song_information } // compare_song_information
private static function clean_string_field_value($value)
{
$value = trim(stripslashes(preg_replace('/\s+/', ' ', $value)));
// Strings containing only UTF-8 BOM = empty string
if (strlen($value) == 2 && (ord($value[0]) == 0xFF || ord($value[0]) == 0xFE)) {
$value = "";
}
return $value;
}
/** /**
* update * update
@ -1683,6 +1712,7 @@ class Song extends database_object implements media, library_item
} }
} }
$sql .= "ORDER BY `date` DESC "; $sql .= "ORDER BY `date` DESC ";
$sql .= "LIMIT " . intval(AmpConfig::get('popular_threshold')) . " ";
$db_results = Dba::read($sql); $db_results = Dba::read($sql);
$results = array(); $results = array();
@ -1692,9 +1722,6 @@ class Song extends database_object implements media, library_item
$row['geo_name'] = Stats::get_cached_place_name($row['latitude'], $row['longitude']); $row['geo_name'] = Stats::get_cached_place_name($row['latitude'], $row['longitude']);
} }
$results[] = $row; $results[] = $row;
if (count($results) >= AmpConfig::get('popular_threshold')) {
break;
}
} }
return $results; return $results;

View file

@ -82,11 +82,11 @@ class Stream
$max_bitrate = AmpConfig::get('max_bit_rate'); $max_bitrate = AmpConfig::get('max_bit_rate');
$min_bitrate = AmpConfig::get('min_bit_rate'); $min_bitrate = AmpConfig::get('min_bit_rate');
// FIXME: This should be configurable for each output type // FIXME: This should be configurable for each output type
$user_sample_rate = AmpConfig::get('sample_rate'); $user_bit_rate = AmpConfig::get('transcode_bitrate');
// If the user's crazy, that's no skin off our back // If the user's crazy, that's no skin off our back
if ($user_sample_rate < $min_bitrate) { if ($user_bit_rate < $min_bitrate) {
$min_bitrate = $user_sample_rate; $min_bitrate = $user_bit_rate;
} }
// Are there site-wide constraints? (Dynamic downsampling.) // Are there site-wide constraints? (Dynamic downsampling.)
@ -109,25 +109,25 @@ class Stream
// We count as one for the algorithm // We count as one for the algorithm
// FIXME: Should this reflect the actual bit rates? // FIXME: Should this reflect the actual bit rates?
$active_streams++; $active_streams++;
$sample_rate = floor($max_bitrate / $active_streams); $bit_rate = floor($max_bitrate / $active_streams);
// Exit if this would be insane // Exit if this would be insane
if ($sample_rate < ($min_bitrate ?: 8)) { if ($bit_rate < ($min_bitrate ?: 8)) {
debug_event('stream', 'Max transcode bandwidth already allocated. Active streams: ' . $active_streams, 2); debug_event('stream', 'Max transcode bandwidth already allocated. Active streams: ' . $active_streams, 2);
header('HTTP/1.1 503 Service Temporarily Unavailable'); header('HTTP/1.1 503 Service Temporarily Unavailable');
exit(); exit();
} }
// Never go over the user's sample rate // Never go over the user's sample rate
if ($sample_rate > $user_sample_rate) { if ($bit_rate > $user_bit_rate) {
$sample_rate = $user_sample_rate; $bit_rate = $user_bit_rate;
} }
} // end if we've got bitrates } // end if we've got bitrates
else { else {
$sample_rate = $user_sample_rate; $bit_rate = $user_bit_rate;
} }
return $sample_rate; return $bit_rate;
} }
/** /**
@ -149,21 +149,21 @@ class Stream
//$media_rate = $media->video_bitrate ?: $media->bitrate; //$media_rate = $media->video_bitrate ?: $media->bitrate;
if (!$options['bitrate']) { if (!$options['bitrate']) {
$sample_rate = self::get_allowed_bitrate($media); $bit_rate = self::get_allowed_bitrate($media);
debug_event('stream', 'Configured bitrate is ' . $sample_rate, 5); debug_event('stream', 'Configured bitrate is ' . $bit_rate, 5);
// Validate the bitrate // Validate the bitrate
$sample_rate = self::validate_bitrate($sample_rate); $bit_rate = self::validate_bitrate($bit_rate);
} else { } else {
$sample_rate = $options['bitrate']; $bit_rate = $options['bitrate'];
} }
// Never upsample a media // Never upsample a media
if ($media->type == $transcode_settings['format'] && ($sample_rate * 1000) > $media->bitrate) { if ($media->type == $transcode_settings['format'] && ($bit_rate * 1000) > $media->bitrate) {
debug_event('stream', 'Clamping bitrate to avoid upsampling to ' . $sample_rate, 5); debug_event('stream', 'Clamping bitrate to avoid upsampling to ' . $bit_rate, 5);
$sample_rate = self::validate_bitrate($media->bitrate / 1000); $bit_rate = self::validate_bitrate($media->bitrate / 1000);
} }
debug_event('stream', 'Final transcode bitrate is ' . $sample_rate, 5); debug_event('stream', 'Final transcode bitrate is ' . $bit_rate, 5);
$song_file = scrub_arg($media->file); $song_file = scrub_arg($media->file);
@ -172,7 +172,8 @@ class Stream
$string_map = array( $string_map = array(
'%FILE%' => $song_file, '%FILE%' => $song_file,
'%SAMPLE%' => $sample_rate '%SAMPLE%' => $bit_rate, // Deprecated
'%BITRATE%' => $bit_rate
); );
if (isset($options['maxbitrate'])) { if (isset($options['maxbitrate'])) {
$string_map['%MAXBITRATE%'] = $options['maxbitrate']; $string_map['%MAXBITRATE%'] = $options['maxbitrate'];
@ -298,9 +299,9 @@ class Stream
public static function validate_bitrate($bitrate) public static function validate_bitrate($bitrate)
{ {
/* Round to standard bitrates */ /* Round to standard bitrates */
$sample_rate = 16*(floor($bitrate/16)); $bit_rate = 16*(floor($bitrate/16));
return $sample_rate; return $bit_rate;
} }
/** /**

View file

@ -429,7 +429,7 @@ class Subsonic_Api
$r = Subsonic_XML_Data::createSuccessResponse(); $r = Subsonic_XML_Data::createSuccessResponse();
$artists = Catalog::get_artists(Catalog::get_catalogs()); $artists = Catalog::get_artists(Catalog::get_catalogs());
Subsonic_XML_Data::addArtistsRoot($r, $artists); Subsonic_XML_Data::addArtistsRoot($r, $artists, true);
self::apiOutput($input, $r); self::apiOutput($input, $r);
} }
@ -755,11 +755,11 @@ class Subsonic_Api
} }
} }
$artistCount = $input['artistCount']; $artistCount = isset($input['artistCount']) ? $input['artistCount'] : 20;
$artistOffset = $input['artistOffset']; $artistOffset = $input['artistOffset'];
$albumCount = $input['albumCount']; $albumCount = isset($input['albumCount']) ? $input['albumCount'] : 20;
$albumOffset = $input['albumOffset']; $albumOffset = $input['albumOffset'];
$songCount = $input['songCount']; $songCount = isset($input['songCount']) ? $input['songCount'] : 20;
$songOffset = $input['songOffset']; $songOffset = $input['songOffset'];
$sartist = array(); $sartist = array();
@ -771,7 +771,9 @@ class Subsonic_Api
$sartist['rule_1_operator'] = $operator; $sartist['rule_1_operator'] = $operator;
$sartist['rule_1'] = "name"; $sartist['rule_1'] = "name";
$sartist['type'] = "artist"; $sartist['type'] = "artist";
$artists = Search::run($sartist); if ($artistCount > 0) {
$artists = Search::run($sartist);
}
$salbum = array(); $salbum = array();
$salbum['limit'] = $albumCount; $salbum['limit'] = $albumCount;
@ -782,7 +784,9 @@ class Subsonic_Api
$salbum['rule_1_operator'] = $operator; $salbum['rule_1_operator'] = $operator;
$salbum['rule_1'] = "title"; $salbum['rule_1'] = "title";
$salbum['type'] = "album"; $salbum['type'] = "album";
$albums = Search::run($salbum); if ($albumCount > 0) {
$albums = Search::run($salbum);
}
$ssong = array(); $ssong = array();
$ssong['limit'] = $songCount; $ssong['limit'] = $songCount;
@ -793,7 +797,9 @@ class Subsonic_Api
$ssong['rule_1_operator'] = $operator; $ssong['rule_1_operator'] = $operator;
$ssong['rule_1'] = "anywhere"; $ssong['rule_1'] = "anywhere";
$ssong['type'] = "song"; $ssong['type'] = "song";
$songs = Search::run($ssong); if ($songCount > 0) {
$songs = Search::run($ssong);
}
$r = Subsonic_XML_Data::createSuccessResponse(); $r = Subsonic_XML_Data::createSuccessResponse();
Subsonic_XML_Data::addSearchResult($r, $artists, $albums, $songs, $elementName); Subsonic_XML_Data::addSearchResult($r, $artists, $albums, $songs, $elementName);
@ -1079,13 +1085,15 @@ class Subsonic_Api
$art = null; $art = null;
if (Subsonic_XML_Data::isArtist($id)) { if (Subsonic_XML_Data::isArtist($id)) {
$art = new Art(Subsonic_XML_Data::getAmpacheId($id), "artist"); $art = new Art(Subsonic_XML_Data::getAmpacheId($id), "artist");
} else { } elseif (Subsonic_XML_Data::isAlbum($id)) {
if (Subsonic_XML_Data::isAlbum($id)) { $art = new Art(Subsonic_XML_Data::getAmpacheId($id), "album");
$art = new Art(Subsonic_XML_Data::getAmpacheId($id), "album"); } elseif (Subsonic_XML_Data::isSong($id)) {
} else { $art = new Art(Subsonic_XML_Data::getAmpacheId($id), "song");
if (Subsonic_XML_Data::isSong($id)) { if ($art != null && $art->id == null) {
$art = new Art(Subsonic_XML_Data::getAmpacheId($id), "song"); // in most cases the song doesn't have a picture, but the album where it belongs to has
} // if this is the case, we take the album art
$song = new Song(Subsonic_XML_Data::getAmpacheId(Subsonic_XML_Data::getAmpacheId($id)));
$art = new Art(Subsonic_XML_Data::getAmpacheId($song->album), "album");
} }
} }

View file

@ -212,13 +212,13 @@ class Subsonic_XML_Data
self::addArtists($xindexes, $artists); self::addArtists($xindexes, $artists);
} }
public static function addArtistsRoot($xml, $artists) public static function addArtistsRoot($xml, $artists, $albumsSet = false)
{ {
$xartists = $xml->addChild('artists'); $xartists = $xml->addChild('artists');
self::addArtists($xartists, $artists, true); self::addArtists($xartists, $artists, true, $albumsSet);
} }
public static function addArtists($xml, $artists, $extra=false) public static function addArtists($xml, $artists, $extra=false, $albumsSet = false)
{ {
$xlastcat = null; $xlastcat = null;
$xsharpcat = null; $xsharpcat = null;
@ -250,25 +250,29 @@ class Subsonic_XML_Data
} }
if ($xlastcat != null) { if ($xlastcat != null) {
self::addArtist($xlastcat, $artist, $extra); self::addArtist($xlastcat, $artist, $extra, false, $albumsSet);
} }
} }
} }
public static function addArtist($xml, $artist, $extra=false, $albums=false) public static function addArtist($xml, $artist, $extra=false, $albums=false, $albumsSet = false)
{ {
$xartist = $xml->addChild('artist'); $xartist = $xml->addChild('artist');
$xartist->addAttribute('id', self::getArtistId($artist->id)); $xartist->addAttribute('id', self::getArtistId($artist->id));
$xartist->addAttribute('name', $artist->name); $xartist->addAttribute('name', $artist->name);
$allalbums = array(); $allalbums = array();
if ($extra || $albums) { if (($extra && !$albumsSet) || $albums) {
$allalbums = $artist->get_albums(null, true); $allalbums = $artist->get_albums(null, true);
} }
if ($extra) { if ($extra) {
//$xartist->addAttribute('coverArt'); //$xartist->addAttribute('coverArt');
$xartist->addAttribute('albumCount', count($allalbums)); if ($albumsSet) {
$xartist->addAttribute('albumCount', $artist->albums);
} else {
$xartist->addAttribute('albumCount', count($allalbums));
}
} }
if ($albums) { if ($albums) {
foreach ($allalbums as $id) { foreach ($allalbums as $id) {
@ -339,6 +343,11 @@ class Subsonic_XML_Data
public static function createSong($xml, $song, $elementName='song') public static function createSong($xml, $song, $elementName='song')
{ {
// Don't create entries for disabled songs
if (!$song->enabled) {
return null;
}
$xsong = $xml->addChild(htmlspecialchars($elementName)); $xsong = $xml->addChild(htmlspecialchars($elementName));
$xsong->addAttribute('id', self::getSongId($song->id)); $xsong->addAttribute('id', self::getSongId($song->id));
$xsong->addAttribute('parent', self::getAlbumId($song->album)); $xsong->addAttribute('parent', self::getAlbumId($song->album));
@ -579,9 +588,11 @@ class Subsonic_XML_Data
$xplaynow = $xml->addChild('nowPlaying'); $xplaynow = $xml->addChild('nowPlaying');
foreach ($data as $d) { foreach ($data as $d) {
$track = self::createSong($xplaynow, $d['media'], "entry"); $track = self::createSong($xplaynow, $d['media'], "entry");
$track->addAttribute('username', $d['client']->username); if ($track !== null) {
$track->addAttribute('minutesAgo', intval(time() - ($d['expire'] - AmpConfig::get('stream_length')) / 1000)); $track->addAttribute('username', $d['client']->username);
$track->addAttribute('playerId', $d['agent']); $track->addAttribute('minutesAgo', intval(time() - ($d['expire'] - AmpConfig::get('stream_length')) / 1000));
$track->addAttribute('playerId', $d['agent']);
}
} }
} }

View file

@ -284,6 +284,7 @@ class Tag extends database_object implements library_item
$uid = ($user == '') ? intval($GLOBALS['user']->id) : intval($user); $uid = ($user == '') ? intval($GLOBALS['user']->id) : intval($user);
$tag_id = intval($tag_id); $tag_id = intval($tag_id);
if (!Core::is_library_item($type)) { if (!Core::is_library_item($type)) {
debug_event('tag.class', $type . " is not a library item.", 3);
return false; return false;
} }
$id = intval($object_id); $id = intval($object_id);
@ -295,7 +296,7 @@ class Tag extends database_object implements library_item
// If tag merged to another one, add reference to the merge destination // If tag merged to another one, add reference to the merge destination
$parent = new Tag($tag_id); $parent = new Tag($tag_id);
$merges = $parent->get_merged_tags(); $merges = $parent->get_merged_tags();
if ($parent->is_hidden === false) { if (!$parent->is_hidden) {
$merges[] = array('id' => $parent->id, 'name' => $parent->name); $merges[] = array('id' => $parent->id, 'name' => $parent->name);
} }
foreach ($merges as $tag) { foreach ($merges as $tag) {
@ -364,7 +365,7 @@ class Tag extends database_object implements library_item
Dba::write($sql, array($this->id)); Dba::write($sql, array($this->id));
$sql = "DELETE FROM `tag` WHERE `tag`.`id` = ? "; $sql = "DELETE FROM `tag` WHERE `tag`.`id` = ? ";
Dba::write($sql, array($this->id, $this->id)); Dba::write($sql, array($this->id));
// Call the garbage collector to clean everything // Call the garbage collector to clean everything
Tag::gc(); Tag::gc();

View file

@ -514,10 +514,13 @@ class Update
$update_string = " - Add theme color option.<br />"; $update_string = " - Add theme color option.<br />";
$version[] = array('version' => '370038','description' => $update_string); $version[] = array('version' => '370038','description' => $update_string);
$update_string = "- Add basic metadata tables<br />"; $update_string = " - Renamed false named sample_rate option name in preference table.<br />";
$version[] = array('version' => '370039', 'description' => $update_string); $version[] = array('version' => '370039','description' => $update_string);
return $version; $update_string = "- Add basic metadata tables<br />";
$version[] = array('version' => '370040', 'description' => $update_string);
return $version;
} }
/** /**
@ -3562,16 +3565,33 @@ class Update
"VALUES ('theme_color','dark','Theme color',0,'special','interface')"; "VALUES ('theme_color','dark','Theme color',0,'special','interface')";
$retval = Dba::write($sql) ? $retval : false; $retval = Dba::write($sql) ? $retval : false;
$id = Dba::insert_id(); $id = Dba::insert_id();
$sql = "INSERT INTO `user_preference` VALUES (-1,?,'0')"; $sql = "INSERT INTO `user_preference` VALUES (-1,?,'dark')";
$retval = Dba::write($sql, array($id)) ? $retval : false; $retval = Dba::write($sql, array($id)) ? $retval : false;
return $retval; return $retval;
} }
/**
* update_370039
*
* Renamed false named sample_rate option name in preference table
*/
public static function update_370039() public static function update_370039()
{ {
$retval = true; $retval = true;
$sql = "UPDATE `preference` SET `name` = 'transcode_bitrate' WHERE `preference`.`name` = 'sample_rate'";
$retval = Dba::write($sql) ? $retval : false;
return $retval;
}
/**
* Adds Metadata tables and preferences
* @return bool
*/
public static function update_370040()
{
$sql = 'CREATE TABLE IF NOT EXISTS `metadata_field` ( $sql = 'CREATE TABLE IF NOT EXISTS `metadata_field` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT, `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL, `name` varchar(255) NOT NULL,

View file

@ -39,8 +39,6 @@ class Upnp_Api
# object.item.textItem # object.item.textItem
# object.container # object.container
const UUIDSTR = '2d8a2e2b-7869-4836-a9ec-76447d620734';
/** /**
* constructor * constructor
* This really isn't anything to do here, so it's private * This really isn't anything to do here, so it's private
@ -48,6 +46,15 @@ class Upnp_Api
private function __construct() private function __construct()
{ {
} }
public static function get_uuidStr()
{
// Create uuid based on host
$key = 'ampache_' . AmpConfig::get('http_host');
$hash = hash('md5', $key);
$uuidstr = substr($hash, 0, 8) . '-' . substr($hash, 8, 4) . '-' . substr($hash, 12, 4) . '-' . substr($hash, 16, 4) . '-' . substr($hash, 20);
return $uuidstr;
}
private static function udpSend($buf, $delay=15, $host="239.255.255.250", $port=1900) private static function udpSend($buf, $delay=15, $host="239.255.255.250", $port=1900)
{ {
@ -63,32 +70,33 @@ class Upnp_Api
$strHeader = 'NOTIFY * HTTP/1.1' . "\r\n"; $strHeader = 'NOTIFY * HTTP/1.1' . "\r\n";
$strHeader .= 'HOST: ' . $host . ':' . $port . "\r\n"; $strHeader .= 'HOST: ' . $host . ':' . $port . "\r\n";
$strHeader .= 'LOCATION: http://' . AmpConfig::get('http_host') . ':'. AmpConfig::get('http_port') . AmpConfig::get('raw_web_path') . '/upnp/MediaServerServiceDesc.php' . "\r\n"; $strHeader .= 'LOCATION: http://' . AmpConfig::get('http_host') . ':'. AmpConfig::get('http_port') . AmpConfig::get('raw_web_path') . '/upnp/MediaServerServiceDesc.php' . "\r\n";
$strHeader .= 'SERVER: DLNADOC/1.50 UPnP/1.0 Ampache/3.8' . "\r\n"; $strHeader .= 'SERVER: DLNADOC/1.50 UPnP/1.0 Ampache/' . AmpConfig::get('version') . "\r\n";
$strHeader .= 'CACHE-CONTROL: max-age=1800' . "\r\n"; $strHeader .= 'CACHE-CONTROL: max-age=1800' . "\r\n";
$strHeader .= 'NTS: ssdp:alive' . "\r\n"; $strHeader .= 'NTS: ssdp:alive' . "\r\n";
$uuidStr = self::get_uuidStr();
$rootDevice = $prefix . ': upnp:rootdevice' . "\r\n"; $rootDevice = $prefix . ': upnp:rootdevice' . "\r\n";
$rootDevice .= 'USN: uuid:' . self::UUIDSTR . '::upnp:rootdevice' . "\r\n". "\r\n"; $rootDevice .= 'USN: uuid:' . $uuidStr . '::upnp:rootdevice' . "\r\n". "\r\n";
$buf = $strHeader . $rootDevice; $buf = $strHeader . $rootDevice;
self::udpSend($buf, $delay, $host, $port); self::udpSend($buf, $delay, $host, $port);
$uuid = $prefix . ': uuid:' . self::UUIDSTR . "\r\n"; $uuid = $prefix . ': uuid:' . $uuidStr . "\r\n";
$uuid .= 'USN: uuid:' . self::UUIDSTR . "\r\n". "\r\n"; $uuid .= 'USN: uuid:' . $uuidStr . "\r\n". "\r\n";
$buf = $strHeader . $uuid; $buf = $strHeader . $uuid;
self::udpSend($buf, $delay, $host, $port); self::udpSend($buf, $delay, $host, $port);
$deviceType = $prefix . ': urn:schemas-upnp-org:device:MediaServer:1' . "\r\n"; $deviceType = $prefix . ': urn:schemas-upnp-org:device:MediaServer:1' . "\r\n";
$deviceType .= 'USN: uuid:' . self::UUIDSTR . '::urn:schemas-upnp-org:device:MediaServer:1' . "\r\n". "\r\n"; $deviceType .= 'USN: uuid:' . $uuidStr . '::urn:schemas-upnp-org:device:MediaServer:1' . "\r\n". "\r\n";
$buf = $strHeader . $deviceType; $buf = $strHeader . $deviceType;
self::udpSend($buf, $delay, $host, $port); self::udpSend($buf, $delay, $host, $port);
$serviceCM = $prefix . ': urn:schemas-upnp-org:service:ConnectionManager:1' . "\r\n"; $serviceCM = $prefix . ': urn:schemas-upnp-org:service:ConnectionManager:1' . "\r\n";
$serviceCM .= 'USN: uuid:' . self::UUIDSTR . '::urn:schemas-upnp-org:service:ConnectionManager:1' . "\r\n". "\r\n"; $serviceCM .= 'USN: uuid:' . $uuidStr . '::urn:schemas-upnp-org:service:ConnectionManager:1' . "\r\n". "\r\n";
$buf = $strHeader . $serviceCM; $buf = $strHeader . $serviceCM;
self::udpSend($buf, $delay, $host, $port); self::udpSend($buf, $delay, $host, $port);
$serviceCD = $prefix . ': urn:schemas-upnp-org:service:ContentDirectory:1' . "\r\n"; $serviceCD = $prefix . ': urn:schemas-upnp-org:service:ContentDirectory:1' . "\r\n";
$serviceCD .= 'USN: uuid:' . self::UUIDSTR . '::urn:schemas-upnp-org:service:ContentDirectory:1' . "\r\n". "\r\n"; $serviceCD .= 'USN: uuid:' . $uuidStr . '::urn:schemas-upnp-org:service:ContentDirectory:1' . "\r\n". "\r\n";
$buf = $strHeader . $serviceCD; $buf = $strHeader . $serviceCD;
self::udpSend($buf, $delay, $host, $port); self::udpSend($buf, $delay, $host, $port);
} }
@ -426,6 +434,30 @@ class Upnp_Api
break; break;
} }
break; break;
case 'live_streams':
switch (count($pathreq)) {
case 1:
$counts = Catalog::count_medias();
$meta = array(
'id' => $root . '/live_streams',
'parentID' => $root,
'restricted' => '1',
'childCount' => $counts['live_streams'],
'dc:title' => T_('Radio Stations'),
'upnp:class' => 'object.container',
);
break;
case 2:
$radio = new Live_Stream($pathreq[1]);
if ($radio->id) {
$radio->format();
$meta = self::_itemLiveStream($radio, $root . '/live_streams');
}
break;
}
break;
default: default:
$meta = array( $meta = array(
@ -590,6 +622,20 @@ class Upnp_Api
break; break;
} }
break; break;
case 'live_streams':
switch (count($pathreq)) {
case 1: // Get radios list
$radios = Live_Stream::get_all_radios();
list($maxCount, $radios) = self::_slice($radios, $start, $count);
foreach ($radios as $radio_id) {
$radio = new Live_Stream($radio_id);
$radio->format();
$mediaItems[] = self::_itemLiveStream($radio, $parent);
}
break;
}
break;
default: default:
$mediaItems[] = self::_musicMetadata('artists'); $mediaItems[] = self::_musicMetadata('artists');
@ -597,6 +643,7 @@ class Upnp_Api
$mediaItems[] = self::_musicMetadata('songs'); $mediaItems[] = self::_musicMetadata('songs');
$mediaItems[] = self::_musicMetadata('playlists'); $mediaItems[] = self::_musicMetadata('playlists');
$mediaItems[] = self::_musicMetadata('smartplaylists'); $mediaItems[] = self::_musicMetadata('smartplaylists');
$mediaItems[] = self::_musicMetadata('live_streams');
break; break;
} }
@ -950,6 +997,27 @@ class Upnp_Api
'description' => self::_replaceSpecialSymbols($song->comment), 'description' => self::_replaceSpecialSymbols($song->comment),
); );
} }
public static function _itemLiveStream($radio, $parent)
{
$api_session = (AmpConfig::get('require_session')) ? Stream::get_session() : false;
$art_url = Art::url($radio->id, 'live_stream', $api_session);
$fileTypesByExt = self::_getFileTypes();
$arrFileType = $fileTypesByExt[$radio->codec];
return array(
'id' => 'amp://music/live_streams/' . $radio->id,
'parentID' => $parent,
'restricted' => '1',
'dc:title' => self::_replaceSpecialSymbols($radio->name),
'upnp:class' => (isset($arrFileType['class'])) ? $arrFileType['class'] : 'object.item.unknownItem',
'upnp:albumArtURI' => $art_url,
'res' => $radio->url,
'protocolInfo' => $arrFileType['mime']
);
}
private static function _itemTVShow($tvshow, $parent) private static function _itemTVShow($tvshow, $parent)
{ {

View file

@ -1015,14 +1015,12 @@ class User extends database_object
if ($details) { if ($details) {
/* Calculate their total Bandwidth Usage */ /* Calculate their total Bandwidth Usage */
$sql = "SELECT `song`.`size` FROM `song` LEFT JOIN `object_count` ON `song`.`id`=`object_count`.`object_id` " . $sql = "SELECT sum(`song`.`size`) as size FROM `song` LEFT JOIN `object_count` ON `song`.`id`=`object_count`.`object_id` " .
"WHERE `object_count`.`user`='$this->id' AND `object_count`.`object_type`='song'"; "WHERE `object_count`.`user`='$this->id' AND `object_count`.`object_type`='song'";
$db_results = Dba::read($sql); $db_results = Dba::read($sql);
$total = 0; $result = Dba::fetch_assoc($db_results);
while ($r = Dba::fetch_assoc($db_results)) { $total = $result['size'];
$total = $total + $r['size'];
}
$this->f_useage = UI::format_bytes($total); $this->f_useage = UI::format_bytes($total);

View file

@ -161,6 +161,25 @@ class Userflag extends database_object
parent::add_to_cache('userflag_' . $this->type . '_user' . $user_id, $this->id, $flagged); parent::add_to_cache('userflag_' . $this->type . '_user' . $user_id, $this->id, $flagged);
// Forward flag to last.fm and Libre.fm (song only)
if ($this->type == 'song') {
$user = new User($user_id);
$song = new Song($this->id);
if ($song) {
$song->format();
foreach (Plugin::get_plugins('save_mediaplay') as $plugin_name) {
try {
$plugin = new Plugin($plugin_name);
if ($plugin->load($user)) {
$plugin->_plugin->set_flag($song, $flagged);
}
} catch (Exception $e) {
debug_event('user.class.php', 'Stats plugin error: ' . $e->getMessage(), '1');
}
}
}
}
return true; return true;
} // set_flag } // set_flag

View file

@ -410,7 +410,8 @@ class vainfo
$info['tvshow_season'] = $info['tvshow_season'] ?: trim($tags['tvshow_season']); $info['tvshow_season'] = $info['tvshow_season'] ?: trim($tags['tvshow_season']);
$info['tvshow_episode'] = $info['tvshow_episode'] ?: trim($tags['tvshow_episode']); $info['tvshow_episode'] = $info['tvshow_episode'] ?: trim($tags['tvshow_episode']);
$info['release_date'] = $info['release_date'] ?: trim($tags['release_date']); $info['release_date'] = $info['release_date'] ?: trim($tags['release_date']);
$info['summary'] = $info['summary'] ?: trim($tags['summary']);
$info['tvshow_art'] = $info['tvshow_art'] ?: trim($tags['tvshow_art']); $info['tvshow_art'] = $info['tvshow_art'] ?: trim($tags['tvshow_art']);
$info['tvshow_season_art'] = $info['tvshow_season_art'] ?: trim($tags['tvshow_season_art']); $info['tvshow_season_art'] = $info['tvshow_season_art'] ?: trim($tags['tvshow_season_art']);
$info['art'] = $info['art'] ?: trim($tags['art']); $info['art'] = $info['art'] ?: trim($tags['art']);
@ -1012,55 +1013,133 @@ class vainfo
return $parsed; return $parsed;
} }
/** /**
* _parse_filename
*
* This function uses the file and directory patterns to pull out extra tag * This function uses the file and directory patterns to pull out extra tag
* information. * information.
* parses TV show name variations:
* 1. title.[date].S#[#]E#[#].ext (Upper/lower case)
* 2. title.[date].#[#]X#[#].ext (both upper/lower case letters
* 3. title.[date].Season #[#] Episode #[#].ext
* 4. title.[date].###.ext (maximum of 9 seasons)
* parse directory path for name, season and episode numbers
* /TV shows/show name [(year)]/[season ]##/##.Episode.Title.ext
* parse movie names:
* title.[date].ext
* /movie title [(date)]/title.ext
*/ */
private function _parse_filename($filename) private function _parse_filename($filepath)
{ {
$origin = $filename; $origin = $filepath;
$results = array(); $results = array();
$season = array();
if (in_array('music', $this->gather_types) || in_array('clip', $this->gather_types)) { $episode = array();
// Correctly detect the slash we need to use here $tvyear = array();
if (strpos($filename, '/') !== false) { $temp = array();
$slash_type = '/'; if (strpos($filepath, '/') !== false) {
$slash_type_preg = $slash_type; $slash_type = '~/~';
$slash_type_preg = trim($slash_type,"~");
} else {
$slash_type = "~\\~";
$slash_type_preg = trim($slash_type,"~") . trim($slash_type,"~");
}
$file = pathinfo($filepath,PATHINFO_FILENAME);
preg_match("~(?<=\(\[\<\{)[1|2][0-9]{3}|[1|2][0-9]{3}~", $filepath,$tvyear);
$results['year'] = !empty($tvyear) ? intval($tvyear[0]) : null;
if (in_array('tvshow', $this->gather_types)) {
if (preg_match("~[Ss](\d+)[Ee](\d+)~", $file, $seasonEpisode)) {
$temp = preg_split("~(((\.|_|\s)[Ss]\d+(\.|_)*[Ee]\d+))~",$file,2);
preg_match("~(?<=[Ss])\d+~", $file, $season);
preg_match("~(?<=[Ee])\d+~", $file, $episode);
} else { } else {
$slash_type = '\\'; if (preg_match("~[\.\s\-\_](\d)[xX](\d{1,2})~", $file, $seasonEpisode)) {
$slash_type_preg = $slash_type . $slash_type; $temp = preg_split("~[\.\_\s\-\_]\d+[xX]\d{2}[\.\s\-\_]*|$~",$file);
preg_match("~\d+(?=[Xx])~", $file, $season);
preg_match("~(?<=[Xx])\d+~", $file, $episode);
} else {
if (preg_match("~[S|s]eason[\_\-\.\s](\d+)[\.\-\s\_]?\s?[e|E]pisode[\s\-\.\_]?(\d+)[\.\s\-\_]?~", $file, $seasonEpisode)) {
$temp = preg_split("~[\.\s\-\_][S|s]eason[\s\-\.\_](\d+)[\.\s\-\_]?\s?[e|E]pisode[\s\-\.\_](\d+)([\s\-\.\_])*~",$file,3);
preg_match("~(?<=[Ss]eason[\.\s\-\_])\d+~", $file, $season);
preg_match("~(?<=[Ee]pisode[\.\s\-\_])\d+~", $file, $episode);
} else {
if (preg_match("~[\_\-\.\s](\d)(\d\d)[\_\-\.\s]*~", $file, $seasonEpisode)) {
$temp = preg_split("~[\.\s\-\_](\d)(\d\d)[\.\s\-\_]~",$file);
$season[0] = $seasonEpisode[1];
$episode[0] = $seasonEpisode[2];
}
}
}
}
$results['tvshow_season'] = $season[0];
$results['tvshow_episode'] = $episode[0];
$results['tvshow'] = $this->formatVideoName($temp[0]);
$results['original_name'] = $this->formatVideoName($temp[1]);
// Try to identify the show information from parent folder
if (!$results['tvshow']) {
$folders = preg_split($slash_type, $filepath, -1, PREG_SPLIT_NO_EMPTY);
if ($results['tvshow_season'] && $results['tvshow_episode']) {
// We have season and episode, we assume parent folder is the tvshow name
$filetitle = end($folders);
$results['tvshow'] = $this->formatVideoName($filetitle);
} else {
// Or we assume each parent folder contains one missing information
if (preg_match('/[\/\\\\]([^\/\\\\]*)[\/\\\\]Season (\d{1,2})[\/\\\\]((E|Ep|Episode)\s?(\d{1,2})[\/\\\\])?/i', $filepath, $matches)) {
if ($matches != null) {
$results['tvshow'] = $this->formatVideoName($matches[1]);
$results['tvshow_season'] = $matches[2];
if (isset($matches[5])) {
$results['tvshow_episode'] = $matches[5];
} else {
//match pattern like 10.episode name.mp4
if (preg_match("~^(\d\d)[\_\-\.\s]?(.*)~", $file, $matches)) {
$results['tvshow_episode'] = $matches[1];
$results['original_name'] = $this->formatVideoName($matches[2]);
} else {
//Fallback to match any 3-digit Season/Episode that fails the standard pattern above.
preg_match("~(\d)(\d\d)[\_\-\.\s]*~", $file, $matches);
$results['tvshow_episode'] = $matches[2];
}
}
}
}
}
} }
$results['title'] = $results['tvshow'];
}
if (in_array('movie', $this->gather_types)) {
$results['original_name'] = $results['title'] = $this->formatVideoName($file);
}
if (in_array('music', $this->gather_types) || in_array('clip', $this->gather_types)) {
// Combine the patterns // Combine the patterns
$pattern = preg_quote($this->_dir_pattern) . $slash_type_preg . preg_quote($this->_file_pattern); $pattern = preg_quote($this->_dir_pattern) . $slash_type_preg . preg_quote($this->_file_pattern);
// Remove first left directories from filename to match pattern // Remove first left directories from filename to match pattern
$cntslash = substr_count($pattern, preg_quote($slash_type)) + 1; $cntslash = substr_count($pattern, preg_quote($slash_type)) + 1;
$filepart = explode($slash_type, $filename); $filepart = explode($slash_type, $filepath);
if (count($filepart) > $cntslash) { if (count($filepart) > $cntslash) {
$filename = implode($slash_type, array_slice($filepart, count($filepart) - $cntslash)); $filepath = implode($slash_type, array_slice($filepart, count($filepart) - $cntslash));
} }
// Pull out the pattern codes into an array // Pull out the pattern codes into an array
preg_match_all('/\%\w/', $pattern, $elements); preg_match_all('/\%\w/', $pattern, $elements);
// Mangle the pattern by turning the codes into regex captures // Mangle the pattern by turning the codes into regex captures
$pattern = preg_replace('/\%[Ty]/', '([0-9]+?)', $pattern); $pattern = preg_replace('/\%[Ty]/', '([0-9]+?)', $pattern);
$pattern = preg_replace('/\%\w/', '(.+?)', $pattern); $pattern = preg_replace('/\%\w/', '(.+?)', $pattern);
$pattern = str_replace('/', '\/', $pattern); $pattern = str_replace('/', '\/', $pattern);
$pattern = str_replace(' ', '\s', $pattern); $pattern = str_replace(' ', '\s', $pattern);
$pattern = '/' . $pattern . '\..+$/'; $pattern = '/' . $pattern . '\..+$/';
// Pull out our actual matches // Pull out our actual matches
preg_match($pattern, $filename, $matches); preg_match($pattern, $filepath, $matches);
if ($matches != null) { if ($matches != null) {
// The first element is the full match text // The first element is the full match text
$matched = array_shift($matches); $matched = array_shift($matches);
debug_event('vainfo', $pattern . ' matched ' . $matched . ' on ' . $filename, 5); debug_event('vainfo', $pattern . ' matched ' . $matched . ' on ' . $filepath, 5);
// Iterate over what we found // Iterate over what we found
foreach ($matches as $key => $value) { foreach ($matches as $key => $value) {
$new_key = translate_pattern_code($elements['0'][$key]); $new_key = translate_pattern_code($elements['0'][$key]);
@ -1069,162 +1148,34 @@ class vainfo
} }
} }
$results['title'] = $results['title'] ?: basename($filename); $results['title'] = $results['title'] ?: basename($filepath);
if ($this->islocal) { if ($this->islocal) {
$results['size'] = Core::get_filesize(Core::conv_lc_file($origin)); $results['size'] = Core::get_filesize(Core::conv_lc_file($origin));
} }
} }
} }
if (in_array('tvshow', $this->gather_types)) {
$pathinfo = pathinfo($filename);
$filetitle = $pathinfo['filename'];
$results = array_merge($results, $this->parseEpisodeName($filetitle));
if (!$results['tvshow']) {
// Try to identify the show information from parent folder
$filetitle = basename($pathinfo['dirname']);
$results = array_merge($results, $this->parseEpisodeName($filetitle));
if (!$results['tvshow']) {
if ($results['tvshow_season'] && $results['tvshow_episode']) {
// We have season and episode, we assume parent folder is the tvshow name
$pathinfo = pathinfo($pathinfo['dirname']);
$filetitle = basename($pathinfo['dirname']);
$results['tvshow'] = $this->fixSerieName($filetitle);
} else {
// Or we assume each parent folder contains one missing information
if (preg_match('/[\/\\\\]([^\/\\\\]*)[\/\\\\]Season (\d{1,2})[\/\\\\]((E|Ep|Episode)\s?(\d{1,2})[\/\\\\])?/i', $filename, $matches)) {
if ($matches != null) {
$results['tvshow'] = $this->fixSerieName($matches[1]);
$results['tvshow_season'] = $matches[2];
if (isset($matches[5])) {
$results['tvshow_episode'] = $matches[5];
}
}
}
}
}
}
}
if (in_array('movie', $this->gather_types)) {
$pathinfo = pathinfo($filename);
$filetitle = $pathinfo['filename'];
$results['title'] = $this->fixVideoReleaseName($filetitle);
if (!$results['title']) {
// Try to identify the movie information from parent folder
$filetitle = basename($pathinfo['dirname']);
$results['title'] = $this->fixVideoReleaseName($filetitle);
}
}
return $results; return $results;
} }
private function parseEpisodeName($filetitle) private function removeCommonAbbreviations($name)
{ {
$patterns = array( $abbr = explode(",",AmpConfig::get('common_abbr'));
'/(.*)s(\d\d)e(\d\d)(\D.*)/i', $commonabbr = preg_replace("~\n~", '',$abbr);
'/(.*)s(\d\d)(\D)(.*)/i', $commonabbr[] = '[1|2][0-9]{3}'; //Remove release year
'/(.*)\D(\d{1,2})x(\d\d)(\D)(.*)/i',
'/(.*)\D(\d{1,2})x(\d\d)$/i',
'/(\D*)[\.|\-|_](\d)(\d\d)([\.|\-|_]\D.*)/i',
'/(\D*)(\d)[^0-9](\d\d)(\D.*)/i'
);
$results = array(); //scan for brackets, braces, etc and ignore case.
for ($i=0;$i<count($patterns);$i++) { for ($i=0; $i< count($commonabbr);$i++) {
if (preg_match($patterns[$i], $filetitle, $matches)) { $commonabbr[$i] = "~\[*|\(*|\<*|\{*\b(?i)" . trim($commonabbr[$i]) . "\b\]*|\)*|\>*|\}*~";
$name = $this->fixSerieName($matches[1]); }
if (empty($name)) { $string = preg_replace($commonabbr,'',$name);
continue; return $string;
} }
$season = floatval($matches[2]); private function formatVideoName($name)
if ($season == 0) { {
continue; return ucwords(trim($this->removeCommonAbbreviations(str_replace(['.','_','-'], ' ', $name), "\s\t\n\r\0\x0B\.\_\-")));
}
$episode = floatval($matches[3]);
$leftover = $matches[4];
if ($episode == 0) {
// Some malformed string
$leftover = $filetitle;
}
$results['tvshow'] = $name;
$results['tvshow_season'] = $season;
$results['tvshow_episode'] = $episode;
$results['title'] = $this->fixVideoReleaseName($leftover);
break;
}
}
return $results;
} }
private function fixSerieName($name)
{
$name = str_replace('_', ' ', $name);
$name = str_replace('.', ' ', $name);
$name = str_replace(' ', ' ', $name);
$name = $this->removeStartingDashesAndSpaces($name);
$name = $this->removeEndingDashesAndSpaces($name);
return ucwords($name);
}
private function fixVideoReleaseName($name)
{
$commonabbr = array(
'divx', 'xvid', 'dvdrip', 'hdtv', 'lol', 'axxo', 'repack', 'xor',
'pdtv', 'real', 'vtv', 'caph', '2hd', 'proper', 'fqm', 'uncut',
'topaz', 'tvt', 'notv', 'fpn', 'fov', 'orenji', '0tv', 'omicron',
'dsr', 'ws', 'sys', 'crimson', 'wat', 'hiqt', 'internal', 'brrip',
'boheme', 'vost', 'vostfr', 'fastsub', 'addiction'
);
for ($i=0; $i<count($commonabbr); $i++) {
$name = preg_replace('/[\W|_]' . $commonabbr[$i] . '[\W|_](.*)/i', '.', $name);
}
while (strpos($name, '..') !== false) {
$name = preg_replace('/\.\./', '.', $name);
}
$name = preg_replace('/\.\w*$/', ' ', $name);
$name = preg_replace('/\[.*$/', '', $name);
return $this->fixSerieName($name);
}
private function removeStartingDashesAndSpaces($name)
{
if (empty($name)) {
return $name;
}
while (strpos($name, ' ') === 0 || strpos($name, '-') === 0) {
$name = preg_replace('/^ /', '', $name);
$name = preg_replace('/^-/', '', $name);
}
return $name;
}
private function removeEndingDashesAndSpaces($name)
{
if (empty($name)) {
return $name;
}
while (strrpos($name, ' ') === strlen($name) - 1 || strrpos($name, '-') === strlen($name) - 1) {
$name = preg_replace('/ $/', '', $name);
$name = preg_replace('/-$/', '', $name);
}
return $name;
}
/** /**
* set_broken * set_broken

View file

@ -542,7 +542,7 @@ class Video extends database_object implements media, library_item
$release_date = intval($data['release_date']); $release_date = intval($data['release_date']);
// No release date, then release date = production year // No release date, then release date = production year
if (!$release_date && $data['year']) { if (!$release_date && $data['year']) {
$release_date = strtotime($data['year'] . '01-01'); $release_date = strtotime(strval($data['year']) . '-01-01');
} }
$tags = $data['genre']; $tags = $data['genre'];
$channels = intval($data['channels']); $channels = intval($data['channels']);

View file

@ -21,7 +21,7 @@
*/ */
use MusicBrainz\MusicBrainz; use MusicBrainz\MusicBrainz;
use MusicBrainz\Clients\RequestsMbClient; use MusicBrainz\HttpAdapters\RequestsHttpAdapter;
use MusicBrainz\Filters\ArtistFilter; use MusicBrainz\Filters\ArtistFilter;
class Wanted extends database_object class Wanted extends database_object
@ -117,7 +117,7 @@ class Wanted extends database_object
*/ */
public static function get_missing_albums($artist, $mbid='') public static function get_missing_albums($artist, $mbid='')
{ {
$mb = new MusicBrainz(new RequestsMbClient()); $mb = new MusicBrainz(new RequestsHttpAdapter());
$includes = array( $includes = array(
'release-groups' 'release-groups'
); );
@ -140,9 +140,9 @@ class Wanted extends database_object
} else { } else {
if (trim($album->mbid)) { if (trim($album->mbid)) {
$malbum = $mb->lookup('release', $album->mbid, array('release-groups')); $malbum = $mb->lookup('release', $album->mbid, array('release-groups'));
if ($malbum['release-group']) { if ($malbum->{'release-group'}) {
if (!in_array($malbum['release-group']['id'], $owngroups)) { if (!in_array($malbum->{'release-group'}->id, $owngroups)) {
$owngroups[] = $malbum['release-group']['id']; $owngroups[] = $malbum->{'release-group'}->id;
} }
} }
} }
@ -150,43 +150,43 @@ class Wanted extends database_object
} }
} else { } else {
$wartist['mbid'] = $mbid; $wartist['mbid'] = $mbid;
$wartist['name'] = $martist['name']; $wartist['name'] = $martist->name;
parent::add_to_cache('missing_artist', $mbid, $wartist); parent::add_to_cache('missing_artist', $mbid, $wartist);
$wartist = self::get_missing_artist($mbid); $wartist = self::get_missing_artist($mbid);
} }
$results = array(); $results = array();
foreach ($martist['release-groups'] as $group) { foreach ($martist->{'release-groups'} as $group) {
if (in_array(strtolower($group['primary-type']), $types)) { if (in_array(strtolower($group->{'primary-type'}), $types)) {
$add = true; $add = true;
for ($i = 0; $i < count($group['secondary-types']) && $add; ++$i) { for ($i = 0; $i < count($group->{'secondary-types'}) && $add; ++$i) {
$add = in_array(strtolower($group['secondary-types'][$i]), $types); $add = in_array(strtolower($group->{'secondary-types'}[$i]), $types);
} }
if ($add) { if ($add) {
if (!in_array($group['id'], $owngroups)) { if (!in_array($group->id, $owngroups)) {
$wantedid = self::get_wanted($group['id']); $wantedid = self::get_wanted($group->id);
$wanted = new Wanted($wantedid); $wanted = new Wanted($wantedid);
if ($wanted->id) { if ($wanted->id) {
$wanted->format(); $wanted->format();
} else { } else {
$wanted->mbid = $group['id']; $wanted->mbid = $group->id;
if ($artist) { if ($artist) {
$wanted->artist = $artist->id; $wanted->artist = $artist->id;
} else { } else {
$wanted->artist_mbid = $mbid; $wanted->artist_mbid = $mbid;
} }
$wanted->name = $group['title']; $wanted->name = $group->title;
if (!empty($group['first-release-date'])) { if (!empty($group->{'first-release-date'})) {
if (strlen($group['first-release-date']) == 4) { if (strlen($group->{'first-release-date'}) == 4) {
$wanted->year = $group['first-release-date']; $wanted->year = $group->{'first-release-date'};
} else { } else {
$wanted->year = date("Y", strtotime($group['first-release-date'])); $wanted->year = date("Y", strtotime($group->{'first-release-date'}));
} }
} }
$wanted->accepted = false; $wanted->accepted = false;
$wanted->link = AmpConfig::get('web_path') . "/albums.php?action=show_missing&mbid=" . $group['id']; $wanted->link = AmpConfig::get('web_path') . "/albums.php?action=show_missing&mbid=" . $group->id;
if ($artist) { if ($artist) {
$wanted->link .= "&artist=" . $wanted->artist; $wanted->link .= "&artist=" . $wanted->artist;
} else { } else {
@ -217,7 +217,7 @@ class Wanted extends database_object
if (parent::is_cached('missing_artist', $mbid) ) { if (parent::is_cached('missing_artist', $mbid) ) {
$wartist = parent::get_from_cache('missing_artist', $mbid); $wartist = parent::get_from_cache('missing_artist', $mbid);
} else { } else {
$mb = new MusicBrainz(new RequestsMbClient()); $mb = new MusicBrainz(new RequestsHttpAdapter());
$wartist['mbid'] = $mbid; $wartist['mbid'] = $mbid;
$wartist['name'] = T_('Unknown Artist'); $wartist['name'] = T_('Unknown Artist');
@ -227,7 +227,7 @@ class Wanted extends database_object
return $wartist; return $wartist;
} }
$wartist['name'] = $martist['name']; $wartist['name'] = $martist->name;
parent::add_to_cache('missing_artist', $mbid, $wartist); parent::add_to_cache('missing_artist', $mbid, $wartist);
} }
@ -242,7 +242,7 @@ class Wanted extends database_object
'artist' => $name 'artist' => $name
); );
$filter = new ArtistFilter($args); $filter = new ArtistFilter($args);
$mb = new MusicBrainz(new RequestsMbClient()); $mb = new MusicBrainz(new RequestsHttpAdapter());
$res = $mb->search($filter); $res = $mb->search($filter);
$wartists = array(); $wartists = array();
foreach ($res as $r) { foreach ($res as $r) {
@ -308,10 +308,10 @@ class Wanted extends database_object
public static function delete_wanted_release($mbid) public static function delete_wanted_release($mbid)
{ {
if (self::get_accepted_wanted_count() > 0) { if (self::get_accepted_wanted_count() > 0) {
$mb = new MusicBrainz(new RequestsMbClient()); $mb = new MusicBrainz(new RequestsHttpAdapter());
$malbum = $mb->lookup('release', $mbid, array('release-groups')); $malbum = $mb->lookup('release', $mbid, array('release-groups'));
if ($malbum['release-group']) { if ($malbum->{'release-group'}) {
self::delete_wanted($malbum['release-group']); self::delete_wanted($malbum->{'release-group'});
} }
} }
} }
@ -425,29 +425,29 @@ class Wanted extends database_object
*/ */
public function load_all($track_details = true) public function load_all($track_details = true)
{ {
$mb = new MusicBrainz(new RequestsMbClient()); $mb = new MusicBrainz(new RequestsHttpAdapter());
$this->songs = array(); $this->songs = array();
try { try {
$group = $mb->lookup('release-group', $this->mbid, array( 'releases' )); $group = $mb->lookup('release-group', $this->mbid, array( 'releases' ));
// Set fresh data // Set fresh data
$this->name = $group['title']; $this->name = $group->title;
$this->year = date("Y", strtotime($group['first-release-date'])); $this->year = date("Y", strtotime($group->{'first-release-date'}));
// Load from database if already cached // Load from database if already cached
$this->songs = Song_preview::get_song_previews($this->mbid); $this->songs = Song_preview::get_song_previews($this->mbid);
if (count($group['releases']) > 0) { if (count($group->releases) > 0) {
$this->release_mbid = $group['releases'][0]['id']; $this->release_mbid = $group->releases[0]->id;
if ($track_details && count($this->songs) == 0) { if ($track_details && count($this->songs) == 0) {
// Use the first release as reference for track content // Use the first release as reference for track content
$release = $mb->lookup('release', $this->release_mbid, array( 'recordings' )); $release = $mb->lookup('release', $this->release_mbid, array( 'recordings' ));
foreach ($release['media'] as $media) { foreach ($release->media as $media) {
foreach ($media['tracks'] as $track) { foreach ($media->tracks as $track) {
$song = array(); $song = array();
$song['disk'] = $media['position']; $song['disk'] = $media->position;
$song['track'] = $track['number']; $song['track'] = $track->number;
$song['title'] = $track['title']; $song['title'] = $track->title;
$song['mbid'] = $track['id']; $song['mbid'] = $track->id;
if ($this->artist) { if ($this->artist) {
$song['artist'] = $this->artist; $song['artist'] = $this->artist;
} }
@ -467,7 +467,7 @@ class Wanted extends database_object
foreach (Plugin::get_plugins('get_song_preview') as $plugin_name) { foreach (Plugin::get_plugins('get_song_preview') as $plugin_name) {
$plugin = new Plugin($plugin_name); $plugin = new Plugin($plugin_name);
if ($plugin->load($GLOBALS['user'])) { if ($plugin->load($GLOBALS['user'])) {
$song['file'] = $plugin->_plugin->get_song_preview($track['id'], $artist_name, $track['title']); $song['file'] = $plugin->_plugin->get_song_preview($track->id, $artist_name, $track->title);
if ($song['file'] != null) { if ($song['file'] != null) {
break; break;
} }

View file

@ -439,8 +439,8 @@ class XML_Data
foreach ($songs as $song_id) { foreach ($songs as $song_id) {
$song = new Song($song_id); $song = new Song($song_id);
// If the song id is invalid/null // If the song id is invalid/null or disabled
if (!$song->id) { if (!$song->id || !$song->enabled) {
continue; continue;
} }

View file

@ -258,6 +258,11 @@ function return_bytes($val)
return $val; return $val;
} }
function check_dependencies_folder()
{
return file_exists(AmpConfig::get('prefix') . '/lib/vendor');
}
/** /**
* check_config_writable * check_config_writable
* This checks whether we can write the config file * This checks whether we can write the config file

View file

@ -51,11 +51,12 @@ require_once $prefix . '/modules/php-gettext/gettext.inc';
// Define some base level config options // Define some base level config options
AmpConfig::set('prefix', $prefix); AmpConfig::set('prefix', $prefix);
// Register the autoloader // Register autoloaders
spl_autoload_register(array('Core', 'autoload'), true, true); spl_autoload_register(array('Core', 'autoload'), true, true);
$composer_autoload = $prefix . '/lib/vendor/autoload.php';
require_once $prefix . '/modules/requests/Requests.php'; if (file_exists($composer_autoload)) {
Requests::register_autoloader(); require_once $prefix . '/lib/vendor/autoload.php';
}
// Check to see if this is http or https // Check to see if this is http or https
if ((isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' ) if ((isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' )

View file

@ -53,7 +53,7 @@ if (!file_exists($configfile)) {
// Verify that a few important but commonly disabled PHP functions exist and // Verify that a few important but commonly disabled PHP functions exist and
// that we're on a usable version // that we're on a usable version
if (!check_php()) { if (!check_php() || !check_dependencies_folder()) {
$link = $path . '/test.php'; $link = $path . '/test.php';
} }
@ -66,13 +66,13 @@ if (!empty($link)) {
$results['load_time_begin'] = $load_time_begin; $results['load_time_begin'] = $load_time_begin;
/** This is the version.... fluf nothing more... **/ /** This is the version.... fluf nothing more... **/
$results['version'] = '3.8.1-develop'; $results['version'] = '3.8.1-develop';
$results['int_config_version'] = '30'; $results['int_config_version'] = '31';
if (!empty($results['force_ssl'])) { if (!empty($results['force_ssl'])) {
$http_type = 'https://'; $http_type = 'https://';
} }
if ($ow_config) { if (isset($ow_config) && is_array($ow_config)) {
foreach ($ow_config as $key => $value) { foreach ($ow_config as $key => $value) {
$results[$key] = $value; $results[$key] = $value;
} }
@ -91,7 +91,9 @@ $results['web_path'] = $http_type . $results['http_host'] .
$results['web_path']; $results['web_path'];
$results['site_charset'] = $results['site_charset'] ?: 'UTF-8'; $results['site_charset'] = $results['site_charset'] ?: 'UTF-8';
$results['raw_web_path'] = $results['raw_web_path'] ?: '/'; $results['raw_web_path'] = $results['raw_web_path'] ?: '/';
$results['max_upload_size'] = $results['max_upload_size'] ?: 1048576; if (!isset($results['max_upload_size'])) {
$results['max_upload_size'] = 1048576;
}
$_SERVER['SERVER_NAME'] = $_SERVER['SERVER_NAME'] ?: ''; $_SERVER['SERVER_NAME'] = $_SERVER['SERVER_NAME'] ?: '';
if (isset($results['user_ip_cardinality']) && !$results['user_ip_cardinality']) { if (isset($results['user_ip_cardinality']) && !$results['user_ip_cardinality']) {
@ -105,24 +107,12 @@ $results['cookie_life'] = $results['session_cookielife'];
$results['cookie_secure'] = $results['session_cookiesecure']; $results['cookie_secure'] = $results['session_cookiesecure'];
// Library and module includes we can't do with the autoloader // Library and module includes we can't do with the autoloader
require_once $prefix . '/modules/getid3/getid3.php';
require_once $prefix . '/modules/phpmailer/class.phpmailer.php';
require_once $prefix . '/modules/phpmailer/class.smtp.php';
require_once $prefix . '/modules/infotools/AmazonSearchEngine.class.php'; require_once $prefix . '/modules/infotools/AmazonSearchEngine.class.php';
require_once $prefix . '/modules/musicbrainz/MusicBrainz.php';
require_once $prefix . '/modules/musicbrainz/Exception.php';
require_once $prefix . '/modules/musicbrainz/Clients/MbClient.php';
require_once $prefix . '/modules/musicbrainz/Clients/RequestsMbClient.php';
require_once $prefix . '/modules/musicbrainz/Artist.php';
require_once $prefix . '/modules/musicbrainz/Filters/AbstractFilter.php';
require_once $prefix . '/modules/musicbrainz/Filters/FilterInterface.php';
require_once $prefix . '/modules/musicbrainz/Filters/ArtistFilter.php';
require_once $prefix . '/modules/ampacheapi/AmpacheApi.lib.php';
require_once $prefix . '/modules/EchoNest/Autoloader.php'; //require_once $prefix . '/lib/vendor/phpmailer/phpmailer/class.phpmailer.php';
EchoNest_Autoloader::register(); //require_once $prefix . '/lib/vendor/phpmailer/phpmailer/class.smtp.php';
require_once $prefix . '/modules/SabreDAV/autoload.php'; require_once $prefix . '/lib/vendor/bshaffer/php-echonest-api/lib/EchoNest/Autoloader.php';
/* Temp Fixes */ /* Temp Fixes */
$results = Preference::fix_preferences($results); $results = Preference::fix_preferences($results);

View file

@ -1,9 +1,10 @@
# Apache 2.4 # Apache 2.4
<IfModule mod_authz_core.c> <IfModule mod_authz_core.c>
Require all granted Require all granted
</IfModule> </IfModule>
# Apache 2.2 # Apache 2.2
<IfModule mod_access.c> <IfModule mod_access.c>
Order deny,allow
Allow from all Allow from all
</IfModule> </IfModule>

View file

@ -45,10 +45,10 @@ function initTabs()
$(function() { $(function() {
var rightmenu = $("#rightbar"); var rightmenu = $("#rightbar");
var rightsubmenu = $("#rightbar .submenu");
var pos = rightmenu.offset(); var pos = rightmenu.offset();
if (rightmenu.hasClass('rightbar-float')) { if (rightmenu.hasClass('rightbar-float')) {
$(window).scroll(function() { $(window).scroll(function() {
var rightsubmenu = $("#rightbar .submenu");
if ($(this).scrollTop() > (pos.top)) { if ($(this).scrollTop() > (pos.top)) {
rightmenu.addClass('fixedrightbar'); rightmenu.addClass('fixedrightbar');
rightsubmenu.addClass('fixedrightbarsubmenu'); rightsubmenu.addClass('fixedrightbarsubmenu');

View file

@ -50,6 +50,10 @@ function showPlaylistDialog(e, item_type, item_ids) {
width: 300, width: 300,
height: 100, height: 100,
autoOpen: false, autoOpen: false,
position: {
my: "left+10 top",
of: e
},
open: function () { open: function () {
closeplaylist = 1; closeplaylist = 1;
$(document).bind('click', overlayclickclose); $(document).bind('click', overlayclickclose);
@ -67,7 +71,6 @@ function showPlaylistDialog(e, item_type, item_ids) {
} }
}); });
$("#playlistdialog").dialog("option", "position", [e.clientX + 10, e.clientY]);
$("#playlistdialog").dialog("open"); $("#playlistdialog").dialog("open");
closeplaylist = 0; closeplaylist = 0;
} }
@ -104,6 +107,10 @@ function showBroadcastsDialog(e) {
width: 150, width: 150,
height: 70, height: 70,
autoOpen: false, autoOpen: false,
position: {
my: "left-180 top",
of: e
},
open: function () { open: function () {
closebroadcasts = 1; closebroadcasts = 1;
$(document).bind('click', broverlayclickclose); $(document).bind('click', broverlayclickclose);
@ -121,7 +128,6 @@ function showBroadcastsDialog(e) {
} }
}); });
$("#broadcastsdialog").dialog("option", "position", [e.clientX - 180, e.clientY]);
$("#broadcastsdialog").dialog("open"); $("#broadcastsdialog").dialog("open");
closebroadcasts = 0; closebroadcasts = 0;
} }
@ -158,6 +164,10 @@ function showShareDialog(e, object_type, object_id) {
width: 200, width: 200,
height: 90, height: 90,
autoOpen: false, autoOpen: false,
position: {
my: "left+10 top",
of: e
},
open: function () { open: function () {
closeshare = 1; closeshare = 1;
$(document).bind('click', shoverlayclickclose); $(document).bind('click', shoverlayclickclose);
@ -175,7 +185,6 @@ function showShareDialog(e, object_type, object_id) {
} }
}); });
$("#sharedialog").dialog("option", "position", [e.clientX + 10, e.clientY]);
$("#sharedialog").dialog("open"); $("#sharedialog").dialog("open");
closeshare = 0; closeshare = 0;
} }
@ -298,8 +307,6 @@ function showEditDialog(edit_type, edit_id, edit_form_id, edit_title, refresh_ro
show: { effect: "fade", duration: 400 }, show: { effect: "fade", duration: 400 },
open: function () { open: function () {
$(this).load(parent.contentUrl, function() { $(this).load(parent.contentUrl, function() {
$(this).dialog('option', 'position', 'center');
if ($('#edit_tags').length > 0) { if ($('#edit_tags').length > 0) {
$("#edit_tags").tagit({ $("#edit_tags").tagit({
allowSpaces: true, allowSpaces: true,
@ -329,7 +336,7 @@ function showEditDialog(edit_type, edit_id, edit_form_id, edit_title, refresh_ro
} }
$(window).resize(function() { $(window).resize(function() {
$("#editdialog").dialog("option", "position", ['center', 'center']); $("#editdialog").dialog("option", "position", {my: "center", at: "center", of: window});
}); });
function check_inline_song_edit(type, song) { function check_inline_song_edit(type, song) {

View file

@ -55,7 +55,7 @@ function update_preferences($pref_id=0)
/* Some preferences require some extra checks to be performed */ /* Some preferences require some extra checks to be performed */
switch ($name) { switch ($name) {
case 'sample_rate': case 'transcode_bitrate':
$value = Stream::validate_bitrate($value); $value = Stream::validate_bitrate($value);
break; break;
default: default:
@ -390,6 +390,16 @@ function create_preference_input($name,$value)
} }
echo '<select multiple size="5" name="' . $name . '[]">' . implode("\n", $options) . '</select>'; echo '<select multiple size="5" name="' . $name . '[]">' . implode("\n", $options) . '</select>';
break; break;
case 'lastfm_grant_link':
case 'librefm_grant_link':
// construct links for granting access Ampache application to Last.fm and Libre.fm
$plugin_name = ucfirst(str_replace('_grant_link', '', $name));
$plugin = new Plugin($plugin_name);
$url = $plugin->_plugin->url;
$api_key = rawurlencode(AmpConfig::get('lastfm_api_key'));
$callback = rawurlencode(AmpConfig::get('web_path').'/preferences.php?tab=plugins&action=grant&plugin='.$plugin_name);
echo "<a href='$url/api/auth/?api_key=$api_key&cb=$callback'>" . UI::get_icon('plugin', T_("Click for grant Ampache to ").$plugin_name).'</a>';
break;
default: default:
if (preg_match('/_pass$/', $name)) { if (preg_match('/_pass$/', $name)) {
echo '<input type="password" name="' . $name . '" value="******" />'; echo '<input type="password" name="' . $name . '" value="******" />';

View file

@ -37,8 +37,6 @@ function get_themes()
} }
$results = array(); $results = array();
$theme_cfg = '/theme.cfg.php';
while (($f = readdir($handle)) !== false) { while (($f = readdir($handle)) !== false) {
debug_event('theme', "Checking $f", 5); debug_event('theme', "Checking $f", 5);
$cfg = get_theme($f); $cfg = get_theme($f);
@ -77,7 +75,8 @@ function get_theme($name)
$results = parse_ini_file($config_file); $results = parse_ini_file($config_file);
$results['path'] = $name; $results['path'] = $name;
$results['base'] = explode(',', $results['base']); $results['base'] = explode(',', $results['base']);
for ($i = 0; $i < count($results['base']); $i++) { $nbbases = count($results['base']);
for ($i = 0; $i < $nbbases; $i++) {
$results['base'][$i] = explode('|', $results['base'][$i]); $results['base'][$i] = explode('|', $results['base'][$i]);
} }
$results['colors'] = explode(',', $results['colors']); $results['colors'] = explode(',', $results['colors']);

View file

@ -46,6 +46,19 @@ function show_confirmation($title,$text,$next_url,$cancel=0,$form_name='confirma
require AmpConfig::get('prefix') . UI::find_template('show_confirmation.inc.php'); require AmpConfig::get('prefix') . UI::find_template('show_confirmation.inc.php');
} // show_confirmation } // show_confirmation
function catalog_worker($action, $catalogs = null, $options = null)
{
if (AmpConfig::get('ajax_load')) {
$sse_url = AmpConfig::get('web_path') . "/server/sse.server.php?worker=catalog&action=" . $action . "&catalogs=" . urlencode(serialize($catalogs));
if ($options) {
$sse_url .= "&options=" . urlencode(serialize($_POST));
}
sse_worker($sse_url);
} else {
Catalog::process_action($action, $catalogs, $options);
}
}
function sse_worker($url) function sse_worker($url)
{ {
echo '<script type="text/javascript">'; echo '<script type="text/javascript">';

View file

@ -1,121 +0,0 @@
-------------------------------------------------------------------------------
--------- TRANSLATIONS - Ampache Translation Guide -----------
-------------------------------------------------------------------------------
Contents:
1. Introduction
a) Getting the Necessary tools
b) Quick Reference
2. Creating a New Translation
a) Translating
b) Creating a MO file
3. Updating an Existing Translation
a) Merging existing file
b) Generating the MO file
4. Questions?
Introduction:
Ampache uses gettext to handle translating between different languages if
you are interested in translating Ampache into a new language or updating
an existing translations simply follow the directions provided below.
A) Getting the Necessary Tools
Before attempting to translate Ampache into a new language we recommend
contacting us on IRC (chat.freenode.net #ampache) to make sure that nobody else is
already working on a translation. Once you are ready to start your
translation you will need to get a few tools
- Gettext http://www.gnu.org/software/gettext/
- xgettext (Generates PO files)
- msgmerge (Merges old and new PO files)
- msgfmt (Generates the MO file from a PO file)
B) Quick Reference
Below are listed all of the commands you may have to run when working on
a translation
# Gather All info
./gather-messages.sh --all
# Create New po file
LANG=YOURLANG ./gather-messages.sh --init
Example: LANG=ja_JP.UTF-8 ./gather-messages.sh --init
locale/ja_JP/LC_MESSAGES/messages.po will create.
# Merge with existing po file
./gather-messages.sh --merge
# Combine Old & New po files
msgmerge old.po messages.po --output-file=new.po
# Generate MO file for use by gettext
./gather-messages.sh --format
Creating a New Translation:
A) Translating
We do our best to keep an up to date po file in /locale/base feel free to
use this file rather than attempting to generate your own. If you would
like to gather a new PO file simply run /locale/base/gather-messages.sh
(Linux only)
Once you have an up to date PO file you will need to figure out the
country code for the language you are translating into. There are many
lists on the web.
http://www.gnu.org/software/gettext/manual/html_chapter/gettext_16.html
Create the following directory structure and put your po file in the
LC_MESSAGES directory
/locale/<COUNTRY CODE>/LC_MESSAGES/
Start Translating!
C) Creating a MO File
Once you have finished translating the PO file you need to convert it into
a MO file in order for Gettext to be able to use it. Simply run the command
listed below.
msgfmt <DIR>messages.po -o <DIR>/messages.mo
Unfortunately currently Ampache doesn't automatically detect new languages
and thus you have to edit the code directly in order for it to pickup your
new language. Find /lib/preferences.php and then find "case 'lang':" under
the "create_preference_input" function and add a line for your own
language. For example to add en_US support add the following line
echo "\t<option value=\"en_US\" $en_US_lang>" . T_("English") . "</option>\n";
Make sure that it comes after the <select> statement. This will be fixed
for future releases... Sorry :S
Updating an Existing Translation:
A) Merging existing file
If you are updating an existing PO file you will need to merge the new
file with the old so that you don't have to do everything over again.
simply run the following command.
msgmerge old.po messages.po --output-file=new.po
Once you have created the new PO file translate it as you normally would.
B) Generating the MO file
Because this is an existing translation you do not have to modify ampache
code at all simply run.
msgfmt <DIR>messages.po -o <DIR>messages.mo
And then check it in the web interface!
Questions:
If you have any questions or are unable to get gettext to work for you please
feel free to contact us. Thanks!

110
locale/base/TRANSLATIONS.md Normal file
View file

@ -0,0 +1,110 @@
# TRANSLATIONS - Ampache Translation Guide
## Introduction
Ampache uses gettext to handle the translation between different languages.
If you are interested in translating Ampache into a new language or updating
an existing translation, simply follow the instructions provided below.
### A) Getting the Necessary Tools
Since 2015, you can contribute to Ampache's translation without any technical
knowledge or tools, using the online translation platform
[**Transifex**](https://www.transifex.com/ampache/ampache).
Otherwise if you don't want to use the online translation platform,
you should contact us, before you start translating Ampache into a new language,
on IRC (chat.freenode.net #ampache), just to make sure that nobody else is already working on a translation.
Once you are ready to start your translation you will need to get a few tools:
- [Gettext](http://www.gnu.org/software/gettext/)
- xgettext (Generates PO files)
- msgmerge (Merges old and new PO files)
- msgfmt (Generates the MO file from a PO file)
### B) Quick Reference
Below are all of the commands listed you may have to run when working on a translation.
#### Gather All info
./gather-messages.sh --all
#### Create New po file
LANG=YOURLANG ./gather-messages.sh --init
Example:
*LANG=ja_JP.UTF-8 ./gather-messages.sh --init*
locale/ja_JP/LC_MESSAGES/messages.po will create.
#### Merge with existing po file
./gather-messages.sh --merge
#### Combine Old & New po files
msgmerge old.po messages.po --output-file=new.po
#### Generate MO file for use by gettext
./gather-messages.sh --format
## Creating a New Translation
### A) Translating
We do our best to keep an up to date POT file in /locale/base feel free to
use this file rather than attempting to generate your own. If you would
like to gather a new POT file simply run /locale/base/gather-messages.sh
(Linux only)
Once you have an up to date POT file you will need to figure out the
country code for the language you are translating into. There are many
[lists](http://www.gnu.org/software/gettext/manual/html_chapter/gettext_16.html)
on the web.
Create the following directory structure and put your po file in the
LC_MESSAGES directory */locale/<COUNTRY CODE>/LC_MESSAGES/*
Start Translating!
### B) Creating a MO File
Once you have finished translating the PO file, you need to convert it into
a MO file, so Gettext is able to use it.
Simply run the command listed below.
msgfmt <DIR>messages.po -o <DIR>/messages.mo
Unfortunately, currently Ampache doesn't automatically detect new languages
and thus you have to edit the code directly, so Ampache can pickup your
new language.
Find /lib/preferences.php and then find "case 'lang':" under
the "create_preference_input" function and add a line for your own
language. For example to add en_US support add the following line
```php
echo "\t<option value=\"en_US\" $en_US_lang>" . T_("English") . "</option>\n";
```
Make sure that it comes after the `<select>` statement. This will be fixed
for future releases... Sorry :S
## Updating an Existing Translation
### A) Merging existing file
If you are updating an existing PO file you will need to merge your new
created file with the old, existing file. So you don't have to do everything over again.
Simply run the following command.
msgmerge old.po messages.po --output-file=new.po
Once you have created the new PO file, translate it as you normally would.
Hint: [Poedit](https://poedit.net/) is a great, GUI based, cross platform tool to handle this.
It also generates the MO file while you save your work.
### B) Generating the MO file
As this is an existing translation, you do not have to modify Ampache's
code at all. Simply run:
msgfmt <DIR>messages.po -o <DIR>messages.mo
And then check it within the web interface!
## Questions:
If you have any questions or if you are unable to get gettext to work, please
feel free to contact us.
Thanks!

View file

@ -28,20 +28,20 @@ fi
[[ $OLANG ]] || OLANG=$(echo $LANG | sed 's/\..*//;') [[ $OLANG ]] || OLANG=$(echo $LANG | sed 's/\..*//;')
potfile='messages.pot' potfile='messages.pot'
twtxt='translation-words.txt' tdstxt='translatable-database-strings.txt'
ampconf='../../config/ampache.cfg.php' ampconf='../../config/ampache.cfg.php'
usage() { usage() {
echo "" echo ""
echo -e "\033[32m usage: $0 [-h|--help][-g|--get][-gu|--getutw][-i|--init][-m|--merge][-f|--format][-a|--all][-au|--allutw]\033[0m" echo -e "\033[32m usage: $0 [-h|--help][-g|--get][-gu|--getutds][-i|--init][-m|--merge][-f|--format][-a|--all][-au|--allutds]\033[0m"
echo "" echo ""
echo -e "[-g|--get]\t Creates the messages.pot file from translation strings within the source code." echo -e "[-g|--get]\t Creates the messages.pot file from translation strings within the source code."
echo -e "[-gu|--getutw]\t Generates the Pot file from translation strings within the source code\n\t\t and creates or updates the 'translation-words.txt' from the database-preference-table strings.\n\t\t Ampache needs to be fully setup for this to work." echo -e "[-gu|--getutds]\t Generates the Pot file from translation strings within the source code\n\t\t and creates or updates the 'translatable-database-strings.txt' from the database-preference-table strings.\n\t\t Ampache needs to be fully setup for this to work."
echo -e "[-i|--init]\t Creates a new language catalog and its directory structure." echo -e "[-i|--init]\t Creates a new language catalog and its directory structure."
echo -e "[-m|--merge]\t Merges the messages.pot into the language catalogs and shows obsolet translations." echo -e "[-m|--merge]\t Merges the messages.pot into the language catalogs and shows obsolet translations."
echo -e "[-f|--format]\t Compiles the .mo file for its related .po file." echo -e "[-f|--format]\t Compiles the .mo file for its related .po file."
echo -e "[-a|--all]\t Does all except --init and --utw." echo -e "[-a|--all]\t Does all except --init and --utds."
echo -e "[-au|--allutw]\t Does all except --init" echo -e "[-au|--allutds]\t Does all except --init"
echo -e "[-h|--help]\t Shows this help screen." echo -e "[-h|--help]\t Shows this help screen."
echo "" echo ""
echo -e "\033[32m If you encounter any bugs, please report them on Transifex (https://www.transifex.com/projects/p/ampache/)\033[0m" echo -e "\033[32m If you encounter any bugs, please report them on Transifex (https://www.transifex.com/projects/p/ampache/)\033[0m"
@ -60,15 +60,15 @@ generate_pot() {
-o $potfile \ -o $potfile \
$(find ../../ -type f -name \*.php -o -name \*.inc | sort) $(find ../../ -type f -name \*.php -o -name \*.inc | sort)
if [[ $? -eq 0 ]]; then if [[ $? -eq 0 ]]; then
echo -e "\033[32m Pot file creation succeeded. Adding 'translation-words.txt\033[0m" echo -e "\033[32m Pot file creation succeeded. Adding 'translatable-database-strings.txt\033[0m"
cat $twtxt >> $potfile cat $tdstxt >> $potfile
echo -e "\n\033[32m Done, you are able now to use the messages.pot for further translation tasks.\033[0m" echo -e "\n\033[32m Done, you are able now to use the messages.pot for further translation tasks.\033[0m"
else else
echo -e "\033[31m Error\033[0m: Pot file creation has failed!" echo -e "\033[31m Error\033[0m: Pot file creation has failed!"
fi fi
} }
generate_pot_utw() { generate_pot_utds() {
echo "" echo ""
echo "Generating/updating pot-file" echo "Generating/updating pot-file"
echo "" echo ""
@ -80,12 +80,12 @@ generate_pot_utw() {
-o $potfile \ -o $potfile \
$(find ../../ -type f -name \*.php -o -name \*.inc | sort) $(find ../../ -type f -name \*.php -o -name \*.inc | sort)
if [[ $? -eq 0 ]]; then if [[ $? -eq 0 ]]; then
ampconf='../../config/ampache.cfg.php' ampconf='../../config/ampache.cfg.php'
echo -e "\033[32m Pot creation/update successful\033[0m\n" echo -e "\033[32m Pot creation/update successful\033[0m\n"
echo -e "Reading database login information from Amapche config file\n" echo -e "Reading database login information from Amapche config file\n"
dbhost=$(grep -oP "(?<=database_hostname = \")[^\"\n]+" $ampconf) dbhost=$(grep -oP "(?<=database_hostname = \")[^\"\n]+" $ampconf)
if [ ! $dbhost ]; then if [ ! $dbhost ]; then
echo -e "\n\033[31m Error\033[0m: No or false database host setting detected!" echo -e "\n\033[31m Error\033[0m: No or false database host setting detected!"
@ -97,7 +97,7 @@ generate_pot_utw() {
fi fi
fi fi
echo "Temporary saved '$dbhost' as your database host" echo "Temporary saved '$dbhost' as your database host"
dbport=$(grep -oP "(?<=database_port = \")[^\"\n]+" $ampconf) dbport=$(grep -oP "(?<=database_port = \")[^\"\n]+" $ampconf)
if [ ! $dbport ]; then if [ ! $dbport ]; then
echo "" echo ""
@ -108,7 +108,7 @@ generate_pot_utw() {
fi fi
fi fi
echo "Temporary saved '$dbport' as your database port" echo "Temporary saved '$dbport' as your database port"
dbname=$(grep -oP "(?<=database_name = \")[^\"\n]+" $ampconf) dbname=$(grep -oP "(?<=database_name = \")[^\"\n]+" $ampconf)
if [ ! $dbname ]; then if [ ! $dbname ]; then
echo "" echo ""
@ -121,7 +121,7 @@ generate_pot_utw() {
fi fi
fi fi
echo "Temporary saved '$dbname' as your database name" echo "Temporary saved '$dbname' as your database name"
dbuser=$(grep -oP "(?<=database_username = \")[^\"\n]+" $ampconf) dbuser=$(grep -oP "(?<=database_username = \")[^\"\n]+" $ampconf)
if [ ! $dbuser ]; then if [ ! $dbuser ]; then
echo -e "\n\033[31m Error\033[0m: You need to set a valid database user in you Ampache config file" echo -e "\n\033[31m Error\033[0m: You need to set a valid database user in you Ampache config file"
@ -146,37 +146,45 @@ generate_pot_utw() {
else else
echo "Temporary saved '$dbpass' as your database password" echo "Temporary saved '$dbpass' as your database password"
fi fi
echo ""
echo "Deleting old translation-words.txt"
echo ""
rm $twtxt
echo -e "Creating new 'translation-words.txt' from database\n" echo ""
echo "Deleting old translatable-database-strings.txt"
echo ""
rm $tdstxt
echo -e "Creating new 'translatable-database-strings.txt' from database\n"
echo -e " #######################################################################\n\n"\
"# This file lists all description strings from your Ampache-database -> preference-table.\n"\
"# Please do not delete or modify the content by yourself. It will be automatically (re)generated\n"\
"# if you run './gather-messages.sh [-gu|--getutds].\n"\
"# Last Update: $(date "+%d.%m.%Y %H:%M:%S %Z")"\
"\n\n"\
"#######################################################################" >> $tdstxt
mysql -N --database=$dbname --host=$dbhost --user=$dbuser --password=$dbpass -se "SELECT id FROM preference" | mysql -N --database=$dbname --host=$dbhost --user=$dbuser --password=$dbpass -se "SELECT id FROM preference" |
while read dbprefid; do while read dbprefid; do
dbprefdesc=$(mysql -N --database=$dbname --host=$dbhost --user=$dbuser --password=$dbpass -se "SELECT description FROM preference where id=$dbprefid") dbprefdesc=$(mysql -N --database=$dbname --host=$dbhost --user=$dbuser --password=$dbpass -se "SELECT description FROM preference where id=$dbprefid")
dbprefdescchk=$(grep "\"$dbprefdesc\"" $potfile) dbprefdescchk=$(grep "\"$dbprefdesc\"" $potfile)
if [ ! "$dbprefdescchk" ]; then if [ ! "$dbprefdescchk" ]; then
echo -e "\n#: Database preference table id $dbprefid" >> $twtxt echo -e "\n#: Database preference table id $dbprefid" >> $tdstxt
echo -e "msgid \"$dbprefdesc\"" >> $twtxt echo -e "msgid \"$dbprefdesc\"" >> $tdstxt
echo -e "msgstr \"\"" >> $twtxt echo -e "msgstr \"\"" >> $tdstxt
else else
echo -e "\n#: Database preference table id $dbprefid" >> $twtxt echo -e "\n#: Database preference table id $dbprefid" >> $tdstxt
echo -e "# is already in the source code\n# but to avoid confusion, it's added and commented" >> $twtxt echo -e "# is already in the source code\n# but to avoid confusion, it's added and commented" >> $tdstxt
echo -e "# msgid \"$dbprefdesc\"" >> $twtxt echo -e "# msgid \"$dbprefdesc\"" >> $tdstxt
echo -e "# msgstr \"\"" >> $twtxt echo -e "# msgstr \"\"" >> $tdstxt
fi fi
done done
echo -e "\033[32m Pot file creation succeeded. Adding 'translation-words.txt\033[0m" echo -e "\033[32m Pot file creation succeeded. Adding 'translatable-database-strings.txt\033[0m"
cat $twtxt >> $potfile cat $tdstxt >> $potfile
echo -e "\n\033[32m Done, you are able now to use the messages.pot for further translation tasks.\033[0m" echo -e "\n\033[32m Done, you are able now to use the messages.pot for further translation tasks.\033[0m"
else else
echo -e "\033[31m Error\033[0m: Pot file creation has failed!" echo -e "\033[31m Error\033[0m: Pot file creation has failed!"
fi fi
} }
do_msgmerge() { do_msgmerge() {
source=$potfile source=$potfile
target="../$1/LC_MESSAGES/messages.po" target="../$1/LC_MESSAGES/messages.po"
@ -199,38 +207,38 @@ fi
case $1 in case $1 in
'-a'|'--all') '-a'|'--all')
generate_pot generate_pot
for i in $(ls ../ | grep -v base); do for i in $(ls ../ | grep -v base); do
do_msgmerge $i do_msgmerge $i
do_msgfmt $i do_msgfmt $i
done done
;; ;;
'-au'|'--allutw') '-au'|'--allutds')
generate_pot_utw generate_pot_utds
for i in $(ls ../ | grep -v base); do for i in $(ls ../ | grep -v base); do
do_msgmerge $i do_msgmerge $i
do_msgfmt $i do_msgfmt $i
done done
;; ;;
'-af'|'--allformat') '-af'|'--allformat')
for i in $(ls ../ | grep -v base); do for i in $(ls ../ | grep -v base); do
do_msgfmt $i do_msgfmt $i
done done
;; ;;
'-am'|'--allmerge') '-am'|'--allmerge')
for i in $(ls ../ | grep -v base); do for i in $(ls ../ | grep -v base); do
do_msgmerge $i do_msgmerge $i
done done
;; ;;
'-g'|'--get') '-g'|'--get')
generate_pot generate_pot
;; ;;
'-gu'|'--getutw') '-gu'|'--getutds')
generate_pot_utw generate_pot_utds
;; ;;
'-i'|'--init'|'init') '-i'|'--init'|'init')
outdir="../$OLANG/LC_MESSAGES" outdir="../$OLANG/LC_MESSAGES"
[[ -d $outdir ]] || mkdir -p $outdir [[ -d $outdir ]] || mkdir -p $outdir
msginit -l $LANG -i $potfile -o $outdir/messages.po msginit -l $LANG -i $potfile -o $outdir/messages.po
;; ;;
'-f'|'--format'|'format') '-f'|'--format'|'format')
do_msgfmt $OLANG do_msgfmt $OLANG

File diff suppressed because it is too large Load diff

View file

@ -1,3 +1,11 @@
#######################################################################
# This file lists all description strings from your Ampache-database -> preference-table.
# Please do not delete or modify the content by yourself. It will be automatically (re)generated
# if you run './gather-messages.sh [-gu|--getutds].
# Last Update: 17.09.2015 23:01:36 CEST
#######################################################################
#: Database preference table id 1 #: Database preference table id 1
msgid "Allow Downloads" msgid "Allow Downloads"
@ -23,10 +31,6 @@ msgstr ""
msgid "Forces Http play regardless of port" msgid "Forces Http play regardless of port"
msgstr "" msgstr ""
#: Database preference table id 25
msgid "Non-Standard Http Port"
msgstr ""
#: Database preference table id 29 #: Database preference table id 29
msgid "Type of Playback" msgid "Type of Playback"
msgstr "" msgstr ""
@ -354,3 +358,7 @@ msgstr ""
#: Database preference table id 144 #: Database preference table id 144
msgid "Receive notifications by email (shouts, private messages, ...)" msgid "Receive notifications by email (shouts, private messages, ...)"
msgstr "" msgstr ""
#: Database preference table id 145
msgid "Theme color"
msgstr ""

View file

@ -1,563 +0,0 @@
<?php
/**
* This is the PHP OpenID library by JanRain, Inc.
*
* This module contains core utility functionality used by the
* library. See Consumer.php and Server.php for the consumer and
* server implementations.
*
* PHP versions 4 and 5
*
* LICENSE: See the COPYING file included in this distribution.
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005-2008 Janrain, Inc.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
*/
/**
* The library version string
*/
define('Auth_OpenID_VERSION', '2.2.2');
/**
* Require the fetcher code.
*/
require_once "Auth/Yadis/PlainHTTPFetcher.php";
require_once "Auth/Yadis/ParanoidHTTPFetcher.php";
require_once "Auth/OpenID/BigMath.php";
require_once "Auth/OpenID/URINorm.php";
/**
* Status code returned by the server when the only option is to show
* an error page, since we do not have enough information to redirect
* back to the consumer. The associated value is an error message that
* should be displayed on an HTML error page.
*
* @see Auth_OpenID_Server
*/
define('Auth_OpenID_LOCAL_ERROR', 'local_error');
/**
* Status code returned when there is an error to return in key-value
* form to the consumer. The caller should return a 400 Bad Request
* response with content-type text/plain and the value as the body.
*
* @see Auth_OpenID_Server
*/
define('Auth_OpenID_REMOTE_ERROR', 'remote_error');
/**
* Status code returned when there is a key-value form OK response to
* the consumer. The value associated with this code is the
* response. The caller should return a 200 OK response with
* content-type text/plain and the value as the body.
*
* @see Auth_OpenID_Server
*/
define('Auth_OpenID_REMOTE_OK', 'remote_ok');
/**
* Status code returned when there is a redirect back to the
* consumer. The value is the URL to redirect back to. The caller
* should return a 302 Found redirect with a Location: header
* containing the URL.
*
* @see Auth_OpenID_Server
*/
define('Auth_OpenID_REDIRECT', 'redirect');
/**
* Status code returned when the caller needs to authenticate the
* user. The associated value is a {@link Auth_OpenID_ServerRequest}
* object that can be used to complete the authentication. If the user
* has taken some authentication action, use the retry() method of the
* {@link Auth_OpenID_ServerRequest} object to complete the request.
*
* @see Auth_OpenID_Server
*/
define('Auth_OpenID_DO_AUTH', 'do_auth');
/**
* Status code returned when there were no OpenID arguments
* passed. This code indicates that the caller should return a 200 OK
* response and display an HTML page that says that this is an OpenID
* server endpoint.
*
* @see Auth_OpenID_Server
*/
define('Auth_OpenID_DO_ABOUT', 'do_about');
/**
* Defines for regexes and format checking.
*/
define('Auth_OpenID_letters',
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
define('Auth_OpenID_digits',
"0123456789");
define('Auth_OpenID_punct',
"!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~");
Auth_OpenID_include_init();
/**
* The OpenID utility function class.
*
* @package OpenID
* @access private
*/
class Auth_OpenID {
/**
* Return true if $thing is an Auth_OpenID_FailureResponse object;
* false if not.
*
* @access private
*/
static function isFailure($thing)
{
return is_a($thing, 'Auth_OpenID_FailureResponse');
}
/**
* Gets the query data from the server environment based on the
* request method used. If GET was used, this looks at
* $_SERVER['QUERY_STRING'] directly. If POST was used, this
* fetches data from the special php://input file stream.
*
* Returns an associative array of the query arguments.
*
* Skips invalid key/value pairs (i.e. keys with no '=value'
* portion).
*
* Returns an empty array if neither GET nor POST was used, or if
* POST was used but php://input cannot be opened.
*
* See background:
* http://lists.openidenabled.com/pipermail/dev/2007-March/000395.html
*
* @access private
*/
static function getQuery($query_str=null)
{
$data = array();
if ($query_str !== null) {
$data = Auth_OpenID::params_from_string($query_str);
} else if (!array_key_exists('REQUEST_METHOD', $_SERVER)) {
// Do nothing.
} else {
// XXX HACK FIXME HORRIBLE.
//
// POSTing to a URL with query parameters is acceptable, but
// we don't have a clean way to distinguish those parameters
// when we need to do things like return_to verification
// which only want to look at one kind of parameter. We're
// going to emulate the behavior of some other environments
// by defaulting to GET and overwriting with POST if POST
// data is available.
$data = Auth_OpenID::params_from_string($_SERVER['QUERY_STRING']);
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$str = file_get_contents('php://input');
if ($str === false) {
$post = array();
} else {
$post = Auth_OpenID::params_from_string($str);
}
$data = array_merge($data, $post);
}
}
return $data;
}
static function params_from_string($str)
{
$chunks = explode("&", $str);
$data = array();
foreach ($chunks as $chunk) {
$parts = explode("=", $chunk, 2);
if (count($parts) != 2) {
continue;
}
list($k, $v) = $parts;
$data[urldecode($k)] = urldecode($v);
}
return $data;
}
/**
* Create dir_name as a directory if it does not exist. If it
* exists, make sure that it is, in fact, a directory. Returns
* true if the operation succeeded; false if not.
*
* @access private
*/
static function ensureDir($dir_name)
{
if (is_dir($dir_name) || @mkdir($dir_name)) {
return true;
} else {
$parent_dir = dirname($dir_name);
// Terminal case; there is no parent directory to create.
if ($parent_dir == $dir_name) {
return true;
}
return (Auth_OpenID::ensureDir($parent_dir) && @mkdir($dir_name));
}
}
/**
* Adds a string prefix to all values of an array. Returns a new
* array containing the prefixed values.
*
* @access private
*/
static function addPrefix($values, $prefix)
{
$new_values = array();
foreach ($values as $s) {
$new_values[] = $prefix . $s;
}
return $new_values;
}
/**
* Convenience function for getting array values. Given an array
* $arr and a key $key, get the corresponding value from the array
* or return $default if the key is absent.
*
* @access private
*/
static function arrayGet($arr, $key, $fallback = null)
{
if (is_array($arr)) {
if (array_key_exists($key, $arr)) {
return $arr[$key];
} else {
return $fallback;
}
} else {
trigger_error("Auth_OpenID::arrayGet (key = ".$key.") expected " .
"array as first parameter, got " .
gettype($arr), E_USER_WARNING);
return false;
}
}
/**
* Replacement for PHP's broken parse_str.
*/
static function parse_str($query)
{
if ($query === null) {
return null;
}
$parts = explode('&', $query);
$new_parts = array();
for ($i = 0; $i < count($parts); $i++) {
$pair = explode('=', $parts[$i]);
if (count($pair) != 2) {
continue;
}
list($key, $value) = $pair;
$new_parts[urldecode($key)] = urldecode($value);
}
return $new_parts;
}
/**
* Implements the PHP 5 'http_build_query' functionality.
*
* @access private
* @param array $data Either an array key/value pairs or an array
* of arrays, each of which holding two values: a key and a value,
* sequentially.
* @return string $result The result of url-encoding the key/value
* pairs from $data into a URL query string
* (e.g. "username=bob&id=56").
*/
static function httpBuildQuery($data)
{
$pairs = array();
foreach ($data as $key => $value) {
if (is_array($value)) {
$pairs[] = urlencode($value[0])."=".urlencode($value[1]);
} else {
$pairs[] = urlencode($key)."=".urlencode($value);
}
}
return implode("&", $pairs);
}
/**
* "Appends" query arguments onto a URL. The URL may or may not
* already have arguments (following a question mark).
*
* @access private
* @param string $url A URL, which may or may not already have
* arguments.
* @param array $args Either an array key/value pairs or an array of
* arrays, each of which holding two values: a key and a value,
* sequentially. If $args is an ordinary key/value array, the
* parameters will be added to the URL in sorted alphabetical order;
* if $args is an array of arrays, their order will be preserved.
* @return string $url The original URL with the new parameters added.
*
*/
static function appendArgs($url, $args)
{
if (count($args) == 0) {
return $url;
}
// Non-empty array; if it is an array of arrays, use
// multisort; otherwise use sort.
if (array_key_exists(0, $args) &&
is_array($args[0])) {
// Do nothing here.
} else {
$keys = array_keys($args);
sort($keys);
$new_args = array();
foreach ($keys as $key) {
$new_args[] = array($key, $args[$key]);
}
$args = $new_args;
}
$sep = '?';
if (strpos($url, '?') !== false) {
$sep = '&';
}
return $url . $sep . Auth_OpenID::httpBuildQuery($args);
}
/**
* Implements python's urlunparse, which is not available in PHP.
* Given the specified components of a URL, this function rebuilds
* and returns the URL.
*
* @access private
* @param string $scheme The scheme (e.g. 'http'). Defaults to 'http'.
* @param string $host The host. Required.
* @param string $port The port.
* @param string $path The path.
* @param string $query The query.
* @param string $fragment The fragment.
* @return string $url The URL resulting from assembling the
* specified components.
*/
static function urlunparse($scheme, $host, $port = null, $path = '/',
$query = '', $fragment = '')
{
if (!$scheme) {
$scheme = 'http';
}
if (!$host) {
return false;
}
if (!$path) {
$path = '';
}
$result = $scheme . "://" . $host;
if ($port) {
$result .= ":" . $port;
}
$result .= $path;
if ($query) {
$result .= "?" . $query;
}
if ($fragment) {
$result .= "#" . $fragment;
}
return $result;
}
/**
* Given a URL, this "normalizes" it by adding a trailing slash
* and / or a leading http:// scheme where necessary. Returns
* null if the original URL is malformed and cannot be normalized.
*
* @access private
* @param string $url The URL to be normalized.
* @return mixed $new_url The URL after normalization, or null if
* $url was malformed.
*/
static function normalizeUrl($url)
{
@$parsed = parse_url($url);
if (!$parsed) {
return null;
}
if (isset($parsed['scheme']) &&
isset($parsed['host'])) {
$scheme = strtolower($parsed['scheme']);
if (!in_array($scheme, array('http', 'https'))) {
return null;
}
} else {
$url = 'http://' . $url;
}
$normalized = Auth_OpenID_urinorm($url);
if ($normalized === null) {
return null;
}
list($defragged, $frag) = Auth_OpenID::urldefrag($normalized);
return $defragged;
}
/**
* Replacement (wrapper) for PHP's intval() because it's broken.
*
* @access private
*/
static function intval($value)
{
$re = "/^\\d+$/";
if (!preg_match($re, $value)) {
return false;
}
return intval($value);
}
/**
* Count the number of bytes in a string independently of
* multibyte support conditions.
*
* @param string $str The string of bytes to count.
* @return int The number of bytes in $str.
*/
static function bytes($str)
{
return strlen(bin2hex($str)) / 2;
}
/**
* Get the bytes in a string independently of multibyte support
* conditions.
*/
static function toBytes($str)
{
$hex = bin2hex($str);
if (!$hex) {
return array();
}
$b = array();
for ($i = 0; $i < strlen($hex); $i += 2) {
$b[] = chr(base_convert(substr($hex, $i, 2), 16, 10));
}
return $b;
}
static function urldefrag($url)
{
$parts = explode("#", $url, 2);
if (count($parts) == 1) {
return array($parts[0], "");
} else {
return $parts;
}
}
static function filter($callback, &$sequence)
{
$result = array();
foreach ($sequence as $item) {
if (call_user_func_array($callback, array($item))) {
$result[] = $item;
}
}
return $result;
}
static function update(&$dest, &$src)
{
foreach ($src as $k => $v) {
$dest[$k] = $v;
}
}
/**
* Wrap PHP's standard error_log functionality. Use this to
* perform all logging. It will interpolate any additional
* arguments into the format string before logging.
*
* @param string $format_string The sprintf format for the message
*/
static function log($format_string)
{
$args = func_get_args();
$message = call_user_func_array('sprintf', $args);
error_log($message);
}
static function autoSubmitHTML($form, $title="OpenId transaction in progress")
{
return("<html>".
"<head><title>".
$title .
"</title></head>".
"<body onload='document.forms[0].submit();'>".
$form .
"<script>".
"var elements = document.forms[0].elements;".
"for (var i = 0; i < elements.length; i++) {".
" elements[i].style.display = \"none\";".
"}".
"</script>".
"</body>".
"</html>");
}
}
/*
* Function to run when this file is included.
* Abstracted to a function to make life easier
* for some PHP optimizers.
*/
function Auth_OpenID_include_init() {
if (Auth_OpenID_getMathLib() === null) {
Auth_OpenID_setNoMathSupport();
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,610 +0,0 @@
<?php
/**
* This module contains code for dealing with associations between
* consumers and servers.
*
* PHP versions 4 and 5
*
* LICENSE: See the COPYING file included in this distribution.
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005-2008 Janrain, Inc.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
*/
/**
* @access private
*/
require_once 'Auth/OpenID/CryptUtil.php';
/**
* @access private
*/
require_once 'Auth/OpenID/KVForm.php';
/**
* @access private
*/
require_once 'Auth/OpenID/HMAC.php';
/**
* This class represents an association between a server and a
* consumer. In general, users of this library will never see
* instances of this object. The only exception is if you implement a
* custom {@link Auth_OpenID_OpenIDStore}.
*
* If you do implement such a store, it will need to store the values
* of the handle, secret, issued, lifetime, and assoc_type instance
* variables.
*
* @package OpenID
*/
class Auth_OpenID_Association {
/**
* This is a HMAC-SHA1 specific value.
*
* @access private
*/
var $SIG_LENGTH = 20;
/**
* The ordering and name of keys as stored by serialize.
*
* @access private
*/
var $assoc_keys = array(
'version',
'handle',
'secret',
'issued',
'lifetime',
'assoc_type'
);
var $_macs = array(
'HMAC-SHA1' => 'Auth_OpenID_HMACSHA1',
'HMAC-SHA256' => 'Auth_OpenID_HMACSHA256'
);
/**
* This is an alternate constructor (factory method) used by the
* OpenID consumer library to create associations. OpenID store
* implementations shouldn't use this constructor.
*
* @access private
*
* @param integer $expires_in This is the amount of time this
* association is good for, measured in seconds since the
* association was issued.
*
* @param string $handle This is the handle the server gave this
* association.
*
* @param string secret This is the shared secret the server
* generated for this association.
*
* @param assoc_type This is the type of association this
* instance represents. The only valid values of this field at
* this time is 'HMAC-SHA1' and 'HMAC-SHA256', but new types may
* be defined in the future.
*
* @return association An {@link Auth_OpenID_Association}
* instance.
*/
static function fromExpiresIn($expires_in, $handle, $secret, $assoc_type)
{
$issued = time();
$lifetime = $expires_in;
return new Auth_OpenID_Association($handle, $secret,
$issued, $lifetime, $assoc_type);
}
/**
* This is the standard constructor for creating an association.
* The library should create all of the necessary associations, so
* this constructor is not part of the external API.
*
* @access private
*
* @param string $handle This is the handle the server gave this
* association.
*
* @param string $secret This is the shared secret the server
* generated for this association.
*
* @param integer $issued This is the time this association was
* issued, in seconds since 00:00 GMT, January 1, 1970. (ie, a
* unix timestamp)
*
* @param integer $lifetime This is the amount of time this
* association is good for, measured in seconds since the
* association was issued.
*
* @param string $assoc_type This is the type of association this
* instance represents. The only valid values of this field at
* this time is 'HMAC-SHA1' and 'HMAC-SHA256', but new types may
* be defined in the future.
*/
function Auth_OpenID_Association(
$handle, $secret, $issued, $lifetime, $assoc_type)
{
if (!in_array($assoc_type,
Auth_OpenID_getSupportedAssociationTypes(), true)) {
$fmt = 'Unsupported association type (%s)';
trigger_error(sprintf($fmt, $assoc_type), E_USER_ERROR);
}
$this->handle = $handle;
$this->secret = $secret;
$this->issued = $issued;
$this->lifetime = $lifetime;
$this->assoc_type = $assoc_type;
}
/**
* This returns the number of seconds this association is still
* valid for, or 0 if the association is no longer valid.
*
* @return integer $seconds The number of seconds this association
* is still valid for, or 0 if the association is no longer valid.
*/
function getExpiresIn($now = null)
{
if ($now == null) {
$now = time();
}
return max(0, $this->issued + $this->lifetime - $now);
}
/**
* This checks to see if two {@link Auth_OpenID_Association}
* instances represent the same association.
*
* @return bool $result true if the two instances represent the
* same association, false otherwise.
*/
function equal($other)
{
return ((gettype($this) == gettype($other))
&& ($this->handle == $other->handle)
&& ($this->secret == $other->secret)
&& ($this->issued == $other->issued)
&& ($this->lifetime == $other->lifetime)
&& ($this->assoc_type == $other->assoc_type));
}
/**
* Convert an association to KV form.
*
* @return string $result String in KV form suitable for
* deserialization by deserialize.
*/
function serialize()
{
$data = array(
'version' => '2',
'handle' => $this->handle,
'secret' => base64_encode($this->secret),
'issued' => strval(intval($this->issued)),
'lifetime' => strval(intval($this->lifetime)),
'assoc_type' => $this->assoc_type
);
assert(array_keys($data) == $this->assoc_keys);
return Auth_OpenID_KVForm::fromArray($data, $strict = true);
}
/**
* Parse an association as stored by serialize(). This is the
* inverse of serialize.
*
* @param string $assoc_s Association as serialized by serialize()
* @return Auth_OpenID_Association $result instance of this class
*/
static function deserialize($class_name, $assoc_s)
{
$pairs = Auth_OpenID_KVForm::toArray($assoc_s, $strict = true);
$keys = array();
$values = array();
foreach ($pairs as $key => $value) {
if (is_array($value)) {
list($key, $value) = $value;
}
$keys[] = $key;
$values[] = $value;
}
$class_vars = get_class_vars($class_name);
$class_assoc_keys = $class_vars['assoc_keys'];
sort($keys);
sort($class_assoc_keys);
if ($keys != $class_assoc_keys) {
trigger_error('Unexpected key values: ' . var_export($keys, true),
E_USER_WARNING);
return null;
}
$version = $pairs['version'];
$handle = $pairs['handle'];
$secret = $pairs['secret'];
$issued = $pairs['issued'];
$lifetime = $pairs['lifetime'];
$assoc_type = $pairs['assoc_type'];
if ($version != '2') {
trigger_error('Unknown version: ' . $version, E_USER_WARNING);
return null;
}
$issued = intval($issued);
$lifetime = intval($lifetime);
$secret = base64_decode($secret);
return new $class_name(
$handle, $secret, $issued, $lifetime, $assoc_type);
}
/**
* Generate a signature for a sequence of (key, value) pairs
*
* @access private
* @param array $pairs The pairs to sign, in order. This is an
* array of two-tuples.
* @return string $signature The binary signature of this sequence
* of pairs
*/
function sign($pairs)
{
$kv = Auth_OpenID_KVForm::fromArray($pairs);
/* Invalid association types should be caught at constructor */
$callback = $this->_macs[$this->assoc_type];
return call_user_func_array($callback, array($this->secret, $kv));
}
/**
* Generate a signature for some fields in a dictionary
*
* @access private
* @param array $fields The fields to sign, in order; this is an
* array of strings.
* @param array $data Dictionary of values to sign (an array of
* string => string pairs).
* @return string $signature The signature, base64 encoded
*/
function signMessage($message)
{
if ($message->hasKey(Auth_OpenID_OPENID_NS, 'sig') ||
$message->hasKey(Auth_OpenID_OPENID_NS, 'signed')) {
// Already has a sig
return null;
}
$extant_handle = $message->getArg(Auth_OpenID_OPENID_NS,
'assoc_handle');
if ($extant_handle && ($extant_handle != $this->handle)) {
// raise ValueError("Message has a different association handle")
return null;
}
$signed_message = $message;
$signed_message->setArg(Auth_OpenID_OPENID_NS, 'assoc_handle',
$this->handle);
$message_keys = array_keys($signed_message->toPostArgs());
$signed_list = array();
$signed_prefix = 'openid.';
foreach ($message_keys as $k) {
if (strpos($k, $signed_prefix) === 0) {
$signed_list[] = substr($k, strlen($signed_prefix));
}
}
$signed_list[] = 'signed';
sort($signed_list);
$signed_message->setArg(Auth_OpenID_OPENID_NS, 'signed',
implode(',', $signed_list));
$sig = $this->getMessageSignature($signed_message);
$signed_message->setArg(Auth_OpenID_OPENID_NS, 'sig', $sig);
return $signed_message;
}
/**
* Given a {@link Auth_OpenID_Message}, return the key/value pairs
* to be signed according to the signed list in the message. If
* the message lacks a signed list, return null.
*
* @access private
*/
function _makePairs($message)
{
$signed = $message->getArg(Auth_OpenID_OPENID_NS, 'signed');
if (!$signed || Auth_OpenID::isFailure($signed)) {
// raise ValueError('Message has no signed list: %s' % (message,))
return null;
}
$signed_list = explode(',', $signed);
$pairs = array();
$data = $message->toPostArgs();
foreach ($signed_list as $field) {
$pairs[] = array($field, Auth_OpenID::arrayGet($data,
'openid.' .
$field, ''));
}
return $pairs;
}
/**
* Given an {@link Auth_OpenID_Message}, return the signature for
* the signed list in the message.
*
* @access private
*/
function getMessageSignature($message)
{
$pairs = $this->_makePairs($message);
return base64_encode($this->sign($pairs));
}
/**
* Confirm that the signature of these fields matches the
* signature contained in the data.
*
* @access private
*/
function checkMessageSignature($message)
{
$sig = $message->getArg(Auth_OpenID_OPENID_NS,
'sig');
if (!$sig || Auth_OpenID::isFailure($sig)) {
return false;
}
$calculated_sig = $this->getMessageSignature($message);
return Auth_OpenID_CryptUtil::constEq($calculated_sig, $sig);
}
}
function Auth_OpenID_getSecretSize($assoc_type)
{
if ($assoc_type == 'HMAC-SHA1') {
return 20;
} else if ($assoc_type == 'HMAC-SHA256') {
return 32;
} else {
return null;
}
}
function Auth_OpenID_getAllAssociationTypes()
{
return array('HMAC-SHA1', 'HMAC-SHA256');
}
function Auth_OpenID_getSupportedAssociationTypes()
{
$a = array('HMAC-SHA1');
if (Auth_OpenID_HMACSHA256_SUPPORTED) {
$a[] = 'HMAC-SHA256';
}
return $a;
}
function Auth_OpenID_getSessionTypes($assoc_type)
{
$assoc_to_session = array(
'HMAC-SHA1' => array('DH-SHA1', 'no-encryption'));
if (Auth_OpenID_HMACSHA256_SUPPORTED) {
$assoc_to_session['HMAC-SHA256'] =
array('DH-SHA256', 'no-encryption');
}
return Auth_OpenID::arrayGet($assoc_to_session, $assoc_type, array());
}
function Auth_OpenID_checkSessionType($assoc_type, $session_type)
{
if (!in_array($session_type,
Auth_OpenID_getSessionTypes($assoc_type))) {
return false;
}
return true;
}
function Auth_OpenID_getDefaultAssociationOrder()
{
$order = array();
if (!Auth_OpenID_noMathSupport()) {
$order[] = array('HMAC-SHA1', 'DH-SHA1');
if (Auth_OpenID_HMACSHA256_SUPPORTED) {
$order[] = array('HMAC-SHA256', 'DH-SHA256');
}
}
$order[] = array('HMAC-SHA1', 'no-encryption');
if (Auth_OpenID_HMACSHA256_SUPPORTED) {
$order[] = array('HMAC-SHA256', 'no-encryption');
}
return $order;
}
function Auth_OpenID_getOnlyEncryptedOrder()
{
$result = array();
foreach (Auth_OpenID_getDefaultAssociationOrder() as $pair) {
list($assoc, $session) = $pair;
if ($session != 'no-encryption') {
if (Auth_OpenID_HMACSHA256_SUPPORTED &&
($assoc == 'HMAC-SHA256')) {
$result[] = $pair;
} else if ($assoc != 'HMAC-SHA256') {
$result[] = $pair;
}
}
}
return $result;
}
function Auth_OpenID_getDefaultNegotiator()
{
return new Auth_OpenID_SessionNegotiator(
Auth_OpenID_getDefaultAssociationOrder());
}
function Auth_OpenID_getEncryptedNegotiator()
{
return new Auth_OpenID_SessionNegotiator(
Auth_OpenID_getOnlyEncryptedOrder());
}
/**
* A session negotiator controls the allowed and preferred association
* types and association session types. Both the {@link
* Auth_OpenID_Consumer} and {@link Auth_OpenID_Server} use
* negotiators when creating associations.
*
* You can create and use negotiators if you:
* - Do not want to do Diffie-Hellman key exchange because you use
* transport-layer encryption (e.g. SSL)
*
* - Want to use only SHA-256 associations
*
* - Do not want to support plain-text associations over a non-secure
* channel
*
* It is up to you to set a policy for what kinds of associations to
* accept. By default, the library will make any kind of association
* that is allowed in the OpenID 2.0 specification.
*
* Use of negotiators in the library
* =================================
*
* When a consumer makes an association request, it calls {@link
* getAllowedType} to get the preferred association type and
* association session type.
*
* The server gets a request for a particular association/session type
* and calls {@link isAllowed} to determine if it should create an
* association. If it is supported, negotiation is complete. If it is
* not, the server calls {@link getAllowedType} to get an allowed
* association type to return to the consumer.
*
* If the consumer gets an error response indicating that the
* requested association/session type is not supported by the server
* that contains an assocation/session type to try, it calls {@link
* isAllowed} to determine if it should try again with the given
* combination of association/session type.
*
* @package OpenID
*/
class Auth_OpenID_SessionNegotiator {
function Auth_OpenID_SessionNegotiator($allowed_types)
{
$this->allowed_types = array();
$this->setAllowedTypes($allowed_types);
}
/**
* Set the allowed association types, checking to make sure each
* combination is valid.
*
* @access private
*/
function setAllowedTypes($allowed_types)
{
foreach ($allowed_types as $pair) {
list($assoc_type, $session_type) = $pair;
if (!Auth_OpenID_checkSessionType($assoc_type, $session_type)) {
return false;
}
}
$this->allowed_types = $allowed_types;
return true;
}
/**
* Add an association type and session type to the allowed types
* list. The assocation/session pairs are tried in the order that
* they are added.
*
* @access private
*/
function addAllowedType($assoc_type, $session_type = null)
{
if ($this->allowed_types === null) {
$this->allowed_types = array();
}
if ($session_type === null) {
$available = Auth_OpenID_getSessionTypes($assoc_type);
if (!$available) {
return false;
}
foreach ($available as $session_type) {
$this->addAllowedType($assoc_type, $session_type);
}
} else {
if (Auth_OpenID_checkSessionType($assoc_type, $session_type)) {
$this->allowed_types[] = array($assoc_type, $session_type);
} else {
return false;
}
}
return true;
}
// Is this combination of association type and session type allowed?
function isAllowed($assoc_type, $session_type)
{
$assoc_good = in_array(array($assoc_type, $session_type),
$this->allowed_types);
$matches = in_array($session_type,
Auth_OpenID_getSessionTypes($assoc_type));
return ($assoc_good && $matches);
}
/**
* Get a pair of assocation type and session type that are
* supported.
*/
function getAllowedType()
{
if (!$this->allowed_types) {
return array(null, null);
}
return $this->allowed_types[0];
}
}

View file

@ -1,451 +0,0 @@
<?php
/**
* BigMath: A math library wrapper that abstracts out the underlying
* long integer library.
*
* PHP versions 4 and 5
*
* LICENSE: See the COPYING file included in this distribution.
*
* @access private
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005-2008 Janrain, Inc.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
*/
/**
* Needed for random number generation
*/
require_once 'Auth/OpenID/CryptUtil.php';
/**
* Need Auth_OpenID::bytes().
*/
require_once 'Auth/OpenID.php';
/**
* The superclass of all big-integer math implementations
* @access private
* @package OpenID
*/
class Auth_OpenID_MathLibrary {
/**
* Given a long integer, returns the number converted to a binary
* string. This function accepts long integer values of arbitrary
* magnitude and uses the local large-number math library when
* available.
*
* @param integer $long The long number (can be a normal PHP
* integer or a number created by one of the available long number
* libraries)
* @return string $binary The binary version of $long
*/
function longToBinary($long)
{
$cmp = $this->cmp($long, 0);
if ($cmp < 0) {
$msg = __FUNCTION__ . " takes only positive integers.";
trigger_error($msg, E_USER_ERROR);
return null;
}
if ($cmp == 0) {
return "\x00";
}
$bytes = array();
while ($this->cmp($long, 0) > 0) {
array_unshift($bytes, $this->mod($long, 256));
$long = $this->div($long, pow(2, 8));
}
if ($bytes && ($bytes[0] > 127)) {
array_unshift($bytes, 0);
}
$string = '';
foreach ($bytes as $byte) {
$string .= pack('C', $byte);
}
return $string;
}
/**
* Given a binary string, returns the binary string converted to a
* long number.
*
* @param string $binary The binary version of a long number,
* probably as a result of calling longToBinary
* @return integer $long The long number equivalent of the binary
* string $str
*/
function binaryToLong($str)
{
if ($str === null) {
return null;
}
// Use array_merge to return a zero-indexed array instead of a
// one-indexed array.
$bytes = array_merge(unpack('C*', $str));
$n = $this->init(0);
if ($bytes && ($bytes[0] > 127)) {
trigger_error("bytesToNum works only for positive integers.",
E_USER_WARNING);
return null;
}
foreach ($bytes as $byte) {
$n = $this->mul($n, pow(2, 8));
$n = $this->add($n, $byte);
}
return $n;
}
function base64ToLong($str)
{
$b64 = base64_decode($str);
if ($b64 === false) {
return false;
}
return $this->binaryToLong($b64);
}
function longToBase64($str)
{
return base64_encode($this->longToBinary($str));
}
/**
* Returns a random number in the specified range. This function
* accepts $start, $stop, and $step values of arbitrary magnitude
* and will utilize the local large-number math library when
* available.
*
* @param integer $start The start of the range, or the minimum
* random number to return
* @param integer $stop The end of the range, or the maximum
* random number to return
* @param integer $step The step size, such that $result - ($step
* * N) = $start for some N
* @return integer $result The resulting randomly-generated number
*/
function rand($stop)
{
static $duplicate_cache = array();
// Used as the key for the duplicate cache
$rbytes = $this->longToBinary($stop);
if (array_key_exists($rbytes, $duplicate_cache)) {
list($duplicate, $nbytes) = $duplicate_cache[$rbytes];
} else {
if ($rbytes[0] == "\x00") {
$nbytes = Auth_OpenID::bytes($rbytes) - 1;
} else {
$nbytes = Auth_OpenID::bytes($rbytes);
}
$mxrand = $this->pow(256, $nbytes);
// If we get a number less than this, then it is in the
// duplicated range.
$duplicate = $this->mod($mxrand, $stop);
if (count($duplicate_cache) > 10) {
$duplicate_cache = array();
}
$duplicate_cache[$rbytes] = array($duplicate, $nbytes);
}
do {
$bytes = "\x00" . Auth_OpenID_CryptUtil::getBytes($nbytes);
$n = $this->binaryToLong($bytes);
// Keep looping if this value is in the low duplicated range
} while ($this->cmp($n, $duplicate) < 0);
return $this->mod($n, $stop);
}
}
/**
* Exposes BCmath math library functionality.
*
* {@link Auth_OpenID_BcMathWrapper} wraps the functionality provided
* by the BCMath extension.
*
* @access private
* @package OpenID
*/
class Auth_OpenID_BcMathWrapper extends Auth_OpenID_MathLibrary{
var $type = 'bcmath';
function add($x, $y)
{
return bcadd($x, $y);
}
function sub($x, $y)
{
return bcsub($x, $y);
}
function pow($base, $exponent)
{
return bcpow($base, $exponent);
}
function cmp($x, $y)
{
return bccomp($x, $y);
}
function init($number, $base = 10)
{
return $number;
}
function mod($base, $modulus)
{
return bcmod($base, $modulus);
}
function mul($x, $y)
{
return bcmul($x, $y);
}
function div($x, $y)
{
return bcdiv($x, $y);
}
/**
* Same as bcpowmod when bcpowmod is missing
*
* @access private
*/
function _powmod($base, $exponent, $modulus)
{
$square = $this->mod($base, $modulus);
$result = 1;
while($this->cmp($exponent, 0) > 0) {
if ($this->mod($exponent, 2)) {
$result = $this->mod($this->mul($result, $square), $modulus);
}
$square = $this->mod($this->mul($square, $square), $modulus);
$exponent = $this->div($exponent, 2);
}
return $result;
}
function powmod($base, $exponent, $modulus)
{
if (function_exists('bcpowmod')) {
return bcpowmod($base, $exponent, $modulus);
} else {
return $this->_powmod($base, $exponent, $modulus);
}
}
function toString($num)
{
return $num;
}
}
/**
* Exposes GMP math library functionality.
*
* {@link Auth_OpenID_GmpMathWrapper} wraps the functionality provided
* by the GMP extension.
*
* @access private
* @package OpenID
*/
class Auth_OpenID_GmpMathWrapper extends Auth_OpenID_MathLibrary{
var $type = 'gmp';
function add($x, $y)
{
return gmp_add($x, $y);
}
function sub($x, $y)
{
return gmp_sub($x, $y);
}
function pow($base, $exponent)
{
return gmp_pow($base, $exponent);
}
function cmp($x, $y)
{
return gmp_cmp($x, $y);
}
function init($number, $base = 10)
{
return gmp_init($number, $base);
}
function mod($base, $modulus)
{
return gmp_mod($base, $modulus);
}
function mul($x, $y)
{
return gmp_mul($x, $y);
}
function div($x, $y)
{
return gmp_div_q($x, $y);
}
function powmod($base, $exponent, $modulus)
{
return gmp_powm($base, $exponent, $modulus);
}
function toString($num)
{
return gmp_strval($num);
}
}
/**
* Define the supported extensions. An extension array has keys
* 'modules', 'extension', and 'class'. 'modules' is an array of PHP
* module names which the loading code will attempt to load. These
* values will be suffixed with a library file extension (e.g. ".so").
* 'extension' is the name of a PHP extension which will be tested
* before 'modules' are loaded. 'class' is the string name of a
* {@link Auth_OpenID_MathWrapper} subclass which should be
* instantiated if a given extension is present.
*
* You can define new math library implementations and add them to
* this array.
*/
function Auth_OpenID_math_extensions()
{
$result = array();
if (!defined('Auth_OpenID_BUGGY_GMP')) {
$result[] =
array('modules' => array('gmp', 'php_gmp'),
'extension' => 'gmp',
'class' => 'Auth_OpenID_GmpMathWrapper');
}
$result[] = array('modules' => array('bcmath', 'php_bcmath'),
'extension' => 'bcmath',
'class' => 'Auth_OpenID_BcMathWrapper');
return $result;
}
/**
* Detect which (if any) math library is available
*/
function Auth_OpenID_detectMathLibrary($exts)
{
$loaded = false;
foreach ($exts as $extension) {
if (extension_loaded($extension['extension'])) {
return $extension;
}
}
return false;
}
/**
* {@link Auth_OpenID_getMathLib} checks for the presence of long
* number extension modules and returns an instance of
* {@link Auth_OpenID_MathWrapper} which exposes the module's
* functionality.
*
* Checks for the existence of an extension module described by the
* result of {@link Auth_OpenID_math_extensions()} and returns an
* instance of a wrapper for that extension module. If no extension
* module is found, an instance of {@link Auth_OpenID_MathWrapper} is
* returned, which wraps the native PHP integer implementation. The
* proper calling convention for this method is $lib =
* Auth_OpenID_getMathLib().
*
* This function checks for the existence of specific long number
* implementations in the following order: GMP followed by BCmath.
*
* @return Auth_OpenID_MathWrapper $instance An instance of
* {@link Auth_OpenID_MathWrapper} or one of its subclasses
*
* @package OpenID
*/
function Auth_OpenID_getMathLib()
{
// The instance of Auth_OpenID_MathWrapper that we choose to
// supply will be stored here, so that subseqent calls to this
// method will return a reference to the same object.
static $lib = null;
if (isset($lib)) {
return $lib;
}
if (Auth_OpenID_noMathSupport()) {
$null = null;
return $null;
}
// If this method has not been called before, look at
// Auth_OpenID_math_extensions and try to find an extension that
// works.
$ext = Auth_OpenID_detectMathLibrary(Auth_OpenID_math_extensions());
if ($ext === false) {
$tried = array();
foreach (Auth_OpenID_math_extensions() as $extinfo) {
$tried[] = $extinfo['extension'];
}
$triedstr = implode(", ", $tried);
Auth_OpenID_setNoMathSupport();
$result = null;
return $result;
}
// Instantiate a new wrapper
$class = $ext['class'];
$lib = new $class();
return $lib;
}
function Auth_OpenID_setNoMathSupport()
{
if (!defined('Auth_OpenID_NO_MATH_SUPPORT')) {
define('Auth_OpenID_NO_MATH_SUPPORT', true);
}
}
function Auth_OpenID_noMathSupport()
{
return defined('Auth_OpenID_NO_MATH_SUPPORT');
}

File diff suppressed because it is too large Load diff

View file

@ -1,122 +0,0 @@
<?php
/**
* CryptUtil: A suite of wrapper utility functions for the OpenID
* library.
*
* PHP versions 4 and 5
*
* LICENSE: See the COPYING file included in this distribution.
*
* @access private
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005-2008 Janrain, Inc.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
*/
if (!defined('Auth_OpenID_RAND_SOURCE')) {
/**
* The filename for a source of random bytes. Define this yourself
* if you have a different source of randomness.
*/
define('Auth_OpenID_RAND_SOURCE', '/dev/urandom');
}
class Auth_OpenID_CryptUtil {
/**
* Get the specified number of random bytes.
*
* Attempts to use a cryptographically secure (not predictable)
* source of randomness if available. If there is no high-entropy
* randomness source available, it will fail. As a last resort,
* for non-critical systems, define
* <code>Auth_OpenID_RAND_SOURCE</code> as <code>null</code>, and
* the code will fall back on a pseudo-random number generator.
*
* @param int $num_bytes The length of the return value
* @return string $bytes random bytes
*/
static function getBytes($num_bytes)
{
static $f = null;
$bytes = '';
if ($f === null) {
if (Auth_OpenID_RAND_SOURCE === null) {
$f = false;
} else {
$f = @fopen(Auth_OpenID_RAND_SOURCE, "r");
if ($f === false) {
$msg = 'Define Auth_OpenID_RAND_SOURCE as null to ' .
' continue with an insecure random number generator.';
trigger_error($msg, E_USER_ERROR);
}
}
}
if ($f === false) {
// pseudorandom used
$bytes = '';
for ($i = 0; $i < $num_bytes; $i += 4) {
$bytes .= pack('L', mt_rand());
}
$bytes = substr($bytes, 0, $num_bytes);
} else {
$bytes = fread($f, $num_bytes);
}
return $bytes;
}
/**
* Produce a string of length random bytes, chosen from chrs. If
* $chrs is null, the resulting string may contain any characters.
*
* @param integer $length The length of the resulting
* randomly-generated string
* @param string $chrs A string of characters from which to choose
* to build the new string
* @return string $result A string of randomly-chosen characters
* from $chrs
*/
static function randomString($length, $population = null)
{
if ($population === null) {
return Auth_OpenID_CryptUtil::getBytes($length);
}
$popsize = strlen($population);
if ($popsize > 256) {
$msg = 'More than 256 characters supplied to ' . __FUNCTION__;
trigger_error($msg, E_USER_ERROR);
}
$duplicate = 256 % $popsize;
$str = "";
for ($i = 0; $i < $length; $i++) {
do {
$n = ord(Auth_OpenID_CryptUtil::getBytes(1));
} while ($n < $duplicate);
$n %= $popsize;
$str .= $population[$n];
}
return $str;
}
static function constEq($s1, $s2)
{
if (strlen($s1) != strlen($s2)) {
return false;
}
$result = true;
$length = strlen($s1);
for ($i = 0; $i < $length; $i++) {
$result &= ($s1[$i] == $s2[$i]);
}
return $result;
}
}

View file

@ -1,130 +0,0 @@
<?php
/**
* The Auth_OpenID_DatabaseConnection class, which is used to emulate
* a PEAR database connection.
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005-2008 Janrain, Inc.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
*/
/**
* An empty base class intended to emulate PEAR connection
* functionality in applications that supply their own database
* abstraction mechanisms. See {@link Auth_OpenID_SQLStore} for more
* information. You should subclass this class if you need to create
* an SQL store that needs to access its database using an
* application's database abstraction layer instead of a PEAR database
* connection. Any subclass of Auth_OpenID_DatabaseConnection MUST
* adhere to the interface specified here.
*
* @package OpenID
*/
class Auth_OpenID_DatabaseConnection {
/**
* Sets auto-commit mode on this database connection.
*
* @param bool $mode True if auto-commit is to be used; false if
* not.
*/
function autoCommit($mode)
{
}
/**
* Run an SQL query with the specified parameters, if any.
*
* @param string $sql An SQL string with placeholders. The
* placeholders are assumed to be specific to the database engine
* for this connection.
*
* @param array $params An array of parameters to insert into the
* SQL string using this connection's escaping mechanism.
*
* @return mixed $result The result of calling this connection's
* internal query function. The type of result depends on the
* underlying database engine. This method is usually used when
* the result of a query is not important, like a DDL query.
*/
function query($sql, $params = array())
{
}
/**
* Starts a transaction on this connection, if supported.
*/
function begin()
{
}
/**
* Commits a transaction on this connection, if supported.
*/
function commit()
{
}
/**
* Performs a rollback on this connection, if supported.
*/
function rollback()
{
}
/**
* Run an SQL query and return the first column of the first row
* of the result set, if any.
*
* @param string $sql An SQL string with placeholders. The
* placeholders are assumed to be specific to the database engine
* for this connection.
*
* @param array $params An array of parameters to insert into the
* SQL string using this connection's escaping mechanism.
*
* @return mixed $result The value of the first column of the
* first row of the result set. False if no such result was
* found.
*/
function getOne($sql, $params = array())
{
}
/**
* Run an SQL query and return the first row of the result set, if
* any.
*
* @param string $sql An SQL string with placeholders. The
* placeholders are assumed to be specific to the database engine
* for this connection.
*
* @param array $params An array of parameters to insert into the
* SQL string using this connection's escaping mechanism.
*
* @return array $result The first row of the result set, if any,
* keyed on column name. False if no such result was found.
*/
function getRow($sql, $params = array())
{
}
/**
* Run an SQL query with the specified parameters, if any.
*
* @param string $sql An SQL string with placeholders. The
* placeholders are assumed to be specific to the database engine
* for this connection.
*
* @param array $params An array of parameters to insert into the
* SQL string using this connection's escaping mechanism.
*
* @return array $result An array of arrays representing the
* result of the query; each array is keyed on column name.
*/
function getAll($sql, $params = array())
{
}
}

View file

@ -1,113 +0,0 @@
<?php
/**
* The OpenID library's Diffie-Hellman implementation.
*
* PHP versions 4 and 5
*
* LICENSE: See the COPYING file included in this distribution.
*
* @access private
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005-2008 Janrain, Inc.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
*/
require_once 'Auth/OpenID.php';
require_once 'Auth/OpenID/BigMath.php';
function Auth_OpenID_getDefaultMod()
{
return '155172898181473697471232257763715539915724801'.
'966915404479707795314057629378541917580651227423'.
'698188993727816152646631438561595825688188889951'.
'272158842675419950341258706556549803580104870537'.
'681476726513255747040765857479291291572334510643'.
'245094715007229621094194349783925984760375594985'.
'848253359305585439638443';
}
function Auth_OpenID_getDefaultGen()
{
return '2';
}
/**
* The Diffie-Hellman key exchange class. This class relies on
* {@link Auth_OpenID_MathLibrary} to perform large number operations.
*
* @access private
* @package OpenID
*/
class Auth_OpenID_DiffieHellman {
var $mod;
var $gen;
var $private;
var $lib = null;
function Auth_OpenID_DiffieHellman($mod = null, $gen = null,
$private = null, $lib = null)
{
if ($lib === null) {
$this->lib = Auth_OpenID_getMathLib();
} else {
$this->lib = $lib;
}
if ($mod === null) {
$this->mod = $this->lib->init(Auth_OpenID_getDefaultMod());
} else {
$this->mod = $mod;
}
if ($gen === null) {
$this->gen = $this->lib->init(Auth_OpenID_getDefaultGen());
} else {
$this->gen = $gen;
}
if ($private === null) {
$r = $this->lib->rand($this->mod);
$this->private = $this->lib->add($r, 1);
} else {
$this->private = $private;
}
$this->public = $this->lib->powmod($this->gen, $this->private,
$this->mod);
}
function getSharedSecret($composite)
{
return $this->lib->powmod($composite, $this->private, $this->mod);
}
function getPublicKey()
{
return $this->public;
}
function usingDefaultValues()
{
return ($this->mod == Auth_OpenID_getDefaultMod() &&
$this->gen == Auth_OpenID_getDefaultGen());
}
function xorSecret($composite, $secret, $hash_func)
{
$dh_shared = $this->getSharedSecret($composite);
$dh_shared_str = $this->lib->longToBinary($dh_shared);
$hash_dh_shared = $hash_func($dh_shared_str);
$xsecret = "";
for ($i = 0; $i < Auth_OpenID::bytes($secret); $i++) {
$xsecret .= chr(ord($secret[$i]) ^ ord($hash_dh_shared[$i]));
}
return $xsecret;
}
}

View file

@ -1,606 +0,0 @@
<?php
/**
* The OpenID and Yadis discovery implementation for OpenID 1.2.
*/
require_once "Auth/OpenID.php";
require_once "Auth/OpenID/Parse.php";
require_once "Auth/OpenID/Message.php";
require_once "Auth/Yadis/XRIRes.php";
require_once "Auth/Yadis/Yadis.php";
// XML namespace value
define('Auth_OpenID_XMLNS_1_0', 'http://openid.net/xmlns/1.0');
// Yadis service types
define('Auth_OpenID_TYPE_1_2', 'http://openid.net/signon/1.2');
define('Auth_OpenID_TYPE_1_1', 'http://openid.net/signon/1.1');
define('Auth_OpenID_TYPE_1_0', 'http://openid.net/signon/1.0');
define('Auth_OpenID_TYPE_2_0_IDP', 'http://specs.openid.net/auth/2.0/server');
define('Auth_OpenID_TYPE_2_0', 'http://specs.openid.net/auth/2.0/signon');
define('Auth_OpenID_RP_RETURN_TO_URL_TYPE',
'http://specs.openid.net/auth/2.0/return_to');
function Auth_OpenID_getOpenIDTypeURIs()
{
return array(Auth_OpenID_TYPE_2_0_IDP,
Auth_OpenID_TYPE_2_0,
Auth_OpenID_TYPE_1_2,
Auth_OpenID_TYPE_1_1,
Auth_OpenID_TYPE_1_0);
}
function Auth_OpenID_getOpenIDConsumerTypeURIs()
{
return array(Auth_OpenID_RP_RETURN_TO_URL_TYPE);
}
/*
* Provides a user-readable interpretation of a type uri.
* Useful for error messages.
*/
function Auth_OpenID_getOpenIDTypeName($type_uri) {
switch ($type_uri) {
case Auth_OpenID_TYPE_2_0_IDP:
return 'OpenID 2.0 IDP';
case Auth_OpenID_TYPE_2_0:
return 'OpenID 2.0';
case Auth_OpenID_TYPE_1_2:
return 'OpenID 1.2';
case Auth_OpenID_TYPE_1_1:
return 'OpenID 1.1';
case Auth_OpenID_TYPE_1_0:
return 'OpenID 1.0';
case Auth_OpenID_RP_RETURN_TO_URL_TYPE:
return 'OpenID relying party';
}
}
/**
* Object representing an OpenID service endpoint.
*/
class Auth_OpenID_ServiceEndpoint {
function Auth_OpenID_ServiceEndpoint()
{
$this->claimed_id = null;
$this->server_url = null;
$this->type_uris = array();
$this->local_id = null;
$this->canonicalID = null;
$this->used_yadis = false; // whether this came from an XRDS
$this->display_identifier = null;
}
function getDisplayIdentifier()
{
if ($this->display_identifier) {
return $this->display_identifier;
}
if (! $this->claimed_id) {
return $this->claimed_id;
}
$parsed = parse_url($this->claimed_id);
$scheme = $parsed['scheme'];
$host = $parsed['host'];
$path = $parsed['path'];
if (array_key_exists('query', $parsed)) {
$query = $parsed['query'];
$no_frag = "$scheme://$host$path?$query";
} else {
$no_frag = "$scheme://$host$path";
}
return $no_frag;
}
function usesExtension($extension_uri)
{
return in_array($extension_uri, $this->type_uris);
}
function preferredNamespace()
{
if (in_array(Auth_OpenID_TYPE_2_0_IDP, $this->type_uris) ||
in_array(Auth_OpenID_TYPE_2_0, $this->type_uris)) {
return Auth_OpenID_OPENID2_NS;
} else {
return Auth_OpenID_OPENID1_NS;
}
}
/*
* Query this endpoint to see if it has any of the given type
* URIs. This is useful for implementing other endpoint classes
* that e.g. need to check for the presence of multiple versions
* of a single protocol.
*
* @param $type_uris The URIs that you wish to check
*
* @return all types that are in both in type_uris and
* $this->type_uris
*/
function matchTypes($type_uris)
{
$result = array();
foreach ($type_uris as $test_uri) {
if ($this->supportsType($test_uri)) {
$result[] = $test_uri;
}
}
return $result;
}
function supportsType($type_uri)
{
// Does this endpoint support this type?
return ((in_array($type_uri, $this->type_uris)) ||
(($type_uri == Auth_OpenID_TYPE_2_0) &&
$this->isOPIdentifier()));
}
function compatibilityMode()
{
return $this->preferredNamespace() != Auth_OpenID_OPENID2_NS;
}
function isOPIdentifier()
{
return in_array(Auth_OpenID_TYPE_2_0_IDP, $this->type_uris);
}
static function fromOPEndpointURL($op_endpoint_url)
{
// Construct an OP-Identifier OpenIDServiceEndpoint object for
// a given OP Endpoint URL
$obj = new Auth_OpenID_ServiceEndpoint();
$obj->server_url = $op_endpoint_url;
$obj->type_uris = array(Auth_OpenID_TYPE_2_0_IDP);
return $obj;
}
function parseService($yadis_url, $uri, $type_uris, $service_element)
{
// Set the state of this object based on the contents of the
// service element. Return true if successful, false if not
// (if findOPLocalIdentifier returns false).
$this->type_uris = $type_uris;
$this->server_url = $uri;
$this->used_yadis = true;
if (!$this->isOPIdentifier()) {
$this->claimed_id = $yadis_url;
$this->local_id = Auth_OpenID_findOPLocalIdentifier(
$service_element,
$this->type_uris);
if ($this->local_id === false) {
return false;
}
}
return true;
}
function getLocalID()
{
// Return the identifier that should be sent as the
// openid.identity_url parameter to the server.
if ($this->local_id === null && $this->canonicalID === null) {
return $this->claimed_id;
} else {
if ($this->local_id) {
return $this->local_id;
} else {
return $this->canonicalID;
}
}
}
/*
* Parse the given document as XRDS looking for OpenID consumer services.
*
* @return array of Auth_OpenID_ServiceEndpoint or null if the
* document cannot be parsed.
*/
function consumerFromXRDS($uri, $xrds_text)
{
$xrds =& Auth_Yadis_XRDS::parseXRDS($xrds_text);
if ($xrds) {
$yadis_services =
$xrds->services(array('filter_MatchesAnyOpenIDConsumerType'));
return Auth_OpenID_makeOpenIDEndpoints($uri, $yadis_services);
}
return null;
}
/*
* Parse the given document as XRDS looking for OpenID services.
*
* @return array of Auth_OpenID_ServiceEndpoint or null if the
* document cannot be parsed.
*/
static function fromXRDS($uri, $xrds_text)
{
$xrds = Auth_Yadis_XRDS::parseXRDS($xrds_text);
if ($xrds) {
$yadis_services =
$xrds->services(array('filter_MatchesAnyOpenIDType'));
return Auth_OpenID_makeOpenIDEndpoints($uri, $yadis_services);
}
return null;
}
/*
* Create endpoints from a DiscoveryResult.
*
* @param discoveryResult Auth_Yadis_DiscoveryResult
* @return array of Auth_OpenID_ServiceEndpoint or null if
* endpoints cannot be created.
*/
static function fromDiscoveryResult($discoveryResult)
{
if ($discoveryResult->isXRDS()) {
return Auth_OpenID_ServiceEndpoint::fromXRDS(
$discoveryResult->normalized_uri,
$discoveryResult->response_text);
} else {
return Auth_OpenID_ServiceEndpoint::fromHTML(
$discoveryResult->normalized_uri,
$discoveryResult->response_text);
}
}
static function fromHTML($uri, $html)
{
$discovery_types = array(
array(Auth_OpenID_TYPE_2_0,
'openid2.provider', 'openid2.local_id'),
array(Auth_OpenID_TYPE_1_1,
'openid.server', 'openid.delegate')
);
$services = array();
foreach ($discovery_types as $triple) {
list($type_uri, $server_rel, $delegate_rel) = $triple;
$urls = Auth_OpenID_legacy_discover($html, $server_rel,
$delegate_rel);
if ($urls === false) {
continue;
}
list($delegate_url, $server_url) = $urls;
$service = new Auth_OpenID_ServiceEndpoint();
$service->claimed_id = $uri;
$service->local_id = $delegate_url;
$service->server_url = $server_url;
$service->type_uris = array($type_uri);
$services[] = $service;
}
return $services;
}
function copy()
{
$x = new Auth_OpenID_ServiceEndpoint();
$x->claimed_id = $this->claimed_id;
$x->server_url = $this->server_url;
$x->type_uris = $this->type_uris;
$x->local_id = $this->local_id;
$x->canonicalID = $this->canonicalID;
$x->used_yadis = $this->used_yadis;
return $x;
}
}
function Auth_OpenID_findOPLocalIdentifier($service, $type_uris)
{
// Extract a openid:Delegate value from a Yadis Service element.
// If no delegate is found, returns null. Returns false on
// discovery failure (when multiple delegate/localID tags have
// different values).
$service->parser->registerNamespace('openid',
Auth_OpenID_XMLNS_1_0);
$service->parser->registerNamespace('xrd',
Auth_Yadis_XMLNS_XRD_2_0);
$parser = $service->parser;
$permitted_tags = array();
if (in_array(Auth_OpenID_TYPE_1_1, $type_uris) ||
in_array(Auth_OpenID_TYPE_1_0, $type_uris)) {
$permitted_tags[] = 'openid:Delegate';
}
if (in_array(Auth_OpenID_TYPE_2_0, $type_uris)) {
$permitted_tags[] = 'xrd:LocalID';
}
$local_id = null;
foreach ($permitted_tags as $tag_name) {
$tags = $service->getElements($tag_name);
foreach ($tags as $tag) {
$content = $parser->content($tag);
if ($local_id === null) {
$local_id = $content;
} else if ($local_id != $content) {
return false;
}
}
}
return $local_id;
}
function filter_MatchesAnyOpenIDType($service)
{
$uris = $service->getTypes();
foreach ($uris as $uri) {
if (in_array($uri, Auth_OpenID_getOpenIDTypeURIs())) {
return true;
}
}
return false;
}
function filter_MatchesAnyOpenIDConsumerType(&$service)
{
$uris = $service->getTypes();
foreach ($uris as $uri) {
if (in_array($uri, Auth_OpenID_getOpenIDConsumerTypeURIs())) {
return true;
}
}
return false;
}
function Auth_OpenID_bestMatchingService($service, $preferred_types)
{
// Return the index of the first matching type, or something
// higher if no type matches.
//
// This provides an ordering in which service elements that
// contain a type that comes earlier in the preferred types list
// come before service elements that come later. If a service
// element has more than one type, the most preferred one wins.
foreach ($preferred_types as $index => $typ) {
if (in_array($typ, $service->type_uris)) {
return $index;
}
}
return count($preferred_types);
}
function Auth_OpenID_arrangeByType($service_list, $preferred_types)
{
// Rearrange service_list in a new list so services are ordered by
// types listed in preferred_types. Return the new list.
// Build a list with the service elements in tuples whose
// comparison will prefer the one with the best matching service
$prio_services = array();
foreach ($service_list as $index => $service) {
$prio_services[] = array(Auth_OpenID_bestMatchingService($service,
$preferred_types),
$index, $service);
}
sort($prio_services);
// Now that the services are sorted by priority, remove the sort
// keys from the list.
foreach ($prio_services as $index => $s) {
$prio_services[$index] = $prio_services[$index][2];
}
return $prio_services;
}
// Extract OP Identifier services. If none found, return the rest,
// sorted with most preferred first according to
// OpenIDServiceEndpoint.openid_type_uris.
//
// openid_services is a list of OpenIDServiceEndpoint objects.
//
// Returns a list of OpenIDServiceEndpoint objects."""
function Auth_OpenID_getOPOrUserServices($openid_services)
{
$op_services = Auth_OpenID_arrangeByType($openid_services,
array(Auth_OpenID_TYPE_2_0_IDP));
$openid_services = Auth_OpenID_arrangeByType($openid_services,
Auth_OpenID_getOpenIDTypeURIs());
if ($op_services) {
return $op_services;
} else {
return $openid_services;
}
}
function Auth_OpenID_makeOpenIDEndpoints($uri, $yadis_services)
{
$s = array();
if (!$yadis_services) {
return $s;
}
foreach ($yadis_services as $service) {
$type_uris = $service->getTypes();
$uris = $service->getURIs();
// If any Type URIs match and there is an endpoint URI
// specified, then this is an OpenID endpoint
if ($type_uris &&
$uris) {
foreach ($uris as $service_uri) {
$openid_endpoint = new Auth_OpenID_ServiceEndpoint();
if ($openid_endpoint->parseService($uri,
$service_uri,
$type_uris,
$service)) {
$s[] = $openid_endpoint;
}
}
}
}
return $s;
}
function Auth_OpenID_discoverWithYadis($uri, $fetcher,
$endpoint_filter='Auth_OpenID_getOPOrUserServices',
$discover_function=null)
{
// Discover OpenID services for a URI. Tries Yadis and falls back
// on old-style <link rel='...'> discovery if Yadis fails.
// Might raise a yadis.discover.DiscoveryFailure if no document
// came back for that URI at all. I don't think falling back to
// OpenID 1.0 discovery on the same URL will help, so don't bother
// to catch it.
if ($discover_function === null) {
$discover_function = array('Auth_Yadis_Yadis', 'discover');
}
$openid_services = array();
$response = call_user_func_array($discover_function,
array($uri, $fetcher));
$yadis_url = $response->normalized_uri;
$yadis_services = array();
if ($response->isFailure() && !$response->isXRDS()) {
return array($uri, array());
}
$openid_services = Auth_OpenID_ServiceEndpoint::fromXRDS(
$yadis_url,
$response->response_text);
if (!$openid_services) {
if ($response->isXRDS()) {
return Auth_OpenID_discoverWithoutYadis($uri,
$fetcher);
}
// Try to parse the response as HTML to get OpenID 1.0/1.1
// <link rel="...">
$openid_services = Auth_OpenID_ServiceEndpoint::fromHTML(
$yadis_url,
$response->response_text);
}
$openid_services = call_user_func_array($endpoint_filter,
array($openid_services));
return array($yadis_url, $openid_services);
}
function Auth_OpenID_discoverURI($uri, $fetcher)
{
$uri = Auth_OpenID::normalizeUrl($uri);
return Auth_OpenID_discoverWithYadis($uri, $fetcher);
}
function Auth_OpenID_discoverWithoutYadis($uri, $fetcher)
{
$http_resp = @$fetcher->get($uri);
if ($http_resp->status != 200 and $http_resp->status != 206) {
return array($uri, array());
}
$identity_url = $http_resp->final_url;
// Try to parse the response as HTML to get OpenID 1.0/1.1 <link
// rel="...">
$openid_services = Auth_OpenID_ServiceEndpoint::fromHTML(
$identity_url,
$http_resp->body);
return array($identity_url, $openid_services);
}
function Auth_OpenID_discoverXRI($iname, $fetcher)
{
$resolver = new Auth_Yadis_ProxyResolver($fetcher);
list($canonicalID, $yadis_services) =
$resolver->query($iname,
Auth_OpenID_getOpenIDTypeURIs(),
array('filter_MatchesAnyOpenIDType'));
$openid_services = Auth_OpenID_makeOpenIDEndpoints($iname,
$yadis_services);
$openid_services = Auth_OpenID_getOPOrUserServices($openid_services);
for ($i = 0; $i < count($openid_services); $i++) {
$openid_services[$i]->canonicalID = $canonicalID;
$openid_services[$i]->claimed_id = $canonicalID;
$openid_services[$i]->display_identifier = $iname;
}
// FIXME: returned xri should probably be in some normal form
return array($iname, $openid_services);
}
function Auth_OpenID_discover($uri, $fetcher)
{
// If the fetcher (i.e., PHP) doesn't support SSL, we can't do
// discovery on an HTTPS URL.
if ($fetcher->isHTTPS($uri) && !$fetcher->supportsSSL()) {
return array($uri, array());
}
if (Auth_Yadis_identifierScheme($uri) == 'XRI') {
$result = Auth_OpenID_discoverXRI($uri, $fetcher);
} else {
$result = Auth_OpenID_discoverURI($uri, $fetcher);
}
// If the fetcher doesn't support SSL, we can't interact with
// HTTPS server URLs; remove those endpoints from the list.
if (!$fetcher->supportsSSL()) {
$http_endpoints = array();
list($new_uri, $endpoints) = $result;
foreach ($endpoints as $e) {
if (!$fetcher->isHTTPS($e->server_url)) {
$http_endpoints[] = $e;
}
}
$result = array($new_uri, $http_endpoints);
}
return $result;
}

View file

@ -1,99 +0,0 @@
<?php
/**
* This file supplies a dumb store backend for OpenID servers and
* consumers.
*
* PHP versions 4 and 5
*
* LICENSE: See the COPYING file included in this distribution.
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005-2008 Janrain, Inc.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
*/
/**
* Import the interface for creating a new store class.
*/
require_once 'Auth/OpenID/Interface.php';
require_once 'Auth/OpenID/HMAC.php';
/**
* This is a store for use in the worst case, when you have no way of
* saving state on the consumer site. Using this store makes the
* consumer vulnerable to replay attacks, as it's unable to use
* nonces. Avoid using this store if it is at all possible.
*
* Most of the methods of this class are implementation details.
* Users of this class need to worry only about the constructor.
*
* @package OpenID
*/
class Auth_OpenID_DumbStore extends Auth_OpenID_OpenIDStore {
/**
* Creates a new {@link Auth_OpenID_DumbStore} instance. For the security
* of the tokens generated by the library, this class attempts to
* at least have a secure implementation of getAuthKey.
*
* When you create an instance of this class, pass in a secret
* phrase. The phrase is hashed with sha1 to make it the correct
* length and form for an auth key. That allows you to use a long
* string as the secret phrase, which means you can make it very
* difficult to guess.
*
* Each {@link Auth_OpenID_DumbStore} instance that is created for use by
* your consumer site needs to use the same $secret_phrase.
*
* @param string secret_phrase The phrase used to create the auth
* key returned by getAuthKey
*/
function Auth_OpenID_DumbStore($secret_phrase)
{
$this->auth_key = Auth_OpenID_SHA1($secret_phrase);
}
/**
* This implementation does nothing.
*/
function storeAssociation($server_url, $association)
{
}
/**
* This implementation always returns null.
*/
function getAssociation($server_url, $handle = null)
{
return null;
}
/**
* This implementation always returns false.
*/
function removeAssociation($server_url, $handle)
{
return false;
}
/**
* In a system truly limited to dumb mode, nonces must all be
* accepted. This therefore always returns true, which makes
* replay attacks feasible.
*/
function useNonce($server_url, $timestamp, $salt)
{
return true;
}
/**
* This method returns the auth key generated by the constructor.
*/
function getAuthKey()
{
return $this->auth_key;
}
}

View file

@ -1,66 +0,0 @@
<?php
/**
* An interface for OpenID extensions.
*
* @package OpenID
*/
/**
* Require the Message implementation.
*/
require_once 'Auth/OpenID/Message.php';
/**
* A base class for accessing extension request and response data for
* the OpenID 2 protocol.
*
* @package OpenID
*/
class Auth_OpenID_Extension {
/**
* ns_uri: The namespace to which to add the arguments for this
* extension
*/
var $ns_uri = null;
var $ns_alias = null;
/**
* Get the string arguments that should be added to an OpenID
* message for this extension.
*/
function getExtensionArgs()
{
return null;
}
/**
* Add the arguments from this extension to the provided message.
*
* Returns the message with the extension arguments added.
*/
function toMessage($message, $request = null)
{
$implicit = $message->isOpenID1();
$added = $message->namespaces->addAlias($this->ns_uri,
$this->ns_alias,
$implicit);
if ($added === null) {
if ($message->namespaces->getAlias($this->ns_uri) !=
$this->ns_alias) {
return null;
}
}
if ($request !== null) {
$message->updateArgs($this->ns_uri,
$this->getExtensionArgs($request));
} else {
$message->updateArgs($this->ns_uri,
$this->getExtensionArgs());
}
return $message;
}
}

View file

@ -1,627 +0,0 @@
<?php
/**
* This file supplies a Memcached store backend for OpenID servers and
* consumers.
*
* PHP versions 4 and 5
*
* LICENSE: See the COPYING file included in this distribution.
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005-2008 Janrain, Inc.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
*/
/**
* Require base class for creating a new interface.
*/
require_once 'Auth/OpenID.php';
require_once 'Auth/OpenID/Interface.php';
require_once 'Auth/OpenID/HMAC.php';
require_once 'Auth/OpenID/Nonce.php';
/**
* This is a filesystem-based store for OpenID associations and
* nonces. This store should be safe for use in concurrent systems on
* both windows and unix (excluding NFS filesystems). There are a
* couple race conditions in the system, but those failure cases have
* been set up in such a way that the worst-case behavior is someone
* having to try to log in a second time.
*
* Most of the methods of this class are implementation details.
* People wishing to just use this store need only pay attention to
* the constructor.
*
* @package OpenID
*/
class Auth_OpenID_FileStore extends Auth_OpenID_OpenIDStore {
/**
* Initializes a new {@link Auth_OpenID_FileStore}. This
* initializes the nonce and association directories, which are
* subdirectories of the directory passed in.
*
* @param string $directory This is the directory to put the store
* directories in.
*/
function Auth_OpenID_FileStore($directory)
{
if (!Auth_OpenID::ensureDir($directory)) {
trigger_error('Not a directory and failed to create: '
. $directory, E_USER_ERROR);
}
$directory = realpath($directory);
$this->directory = $directory;
$this->active = true;
$this->nonce_dir = $directory . DIRECTORY_SEPARATOR . 'nonces';
$this->association_dir = $directory . DIRECTORY_SEPARATOR .
'associations';
// Temp dir must be on the same filesystem as the assciations
// $directory.
$this->temp_dir = $directory . DIRECTORY_SEPARATOR . 'temp';
$this->max_nonce_age = 6 * 60 * 60; // Six hours, in seconds
if (!$this->_setup()) {
trigger_error('Failed to initialize OpenID file store in ' .
$directory, E_USER_ERROR);
}
}
function destroy()
{
Auth_OpenID_FileStore::_rmtree($this->directory);
$this->active = false;
}
/**
* Make sure that the directories in which we store our data
* exist.
*
* @access private
*/
function _setup()
{
return (Auth_OpenID::ensureDir($this->nonce_dir) &&
Auth_OpenID::ensureDir($this->association_dir) &&
Auth_OpenID::ensureDir($this->temp_dir));
}
/**
* Create a temporary file on the same filesystem as
* $this->association_dir.
*
* The temporary directory should not be cleaned if there are any
* processes using the store. If there is no active process using
* the store, it is safe to remove all of the files in the
* temporary directory.
*
* @return array ($fd, $filename)
* @access private
*/
function _mktemp()
{
$name = Auth_OpenID_FileStore::_mkstemp($dir = $this->temp_dir);
$file_obj = @fopen($name, 'wb');
if ($file_obj !== false) {
return array($file_obj, $name);
} else {
Auth_OpenID_FileStore::_removeIfPresent($name);
}
}
function cleanupNonces()
{
global $Auth_OpenID_SKEW;
$nonces = Auth_OpenID_FileStore::_listdir($this->nonce_dir);
$now = time();
$removed = 0;
// Check all nonces for expiry
foreach ($nonces as $nonce_fname) {
$base = basename($nonce_fname);
$parts = explode('-', $base, 2);
$timestamp = $parts[0];
$timestamp = intval($timestamp, 16);
if (abs($timestamp - $now) > $Auth_OpenID_SKEW) {
Auth_OpenID_FileStore::_removeIfPresent($nonce_fname);
$removed += 1;
}
}
return $removed;
}
/**
* Create a unique filename for a given server url and
* handle. This implementation does not assume anything about the
* format of the handle. The filename that is returned will
* contain the domain name from the server URL for ease of human
* inspection of the data directory.
*
* @return string $filename
*/
function getAssociationFilename($server_url, $handle)
{
if (!$this->active) {
trigger_error("FileStore no longer active", E_USER_ERROR);
return null;
}
if (strpos($server_url, '://') === false) {
trigger_error(sprintf("Bad server URL: %s", $server_url),
E_USER_WARNING);
return null;
}
list($proto, $rest) = explode('://', $server_url, 2);
$parts = explode('/', $rest);
$domain = Auth_OpenID_FileStore::_filenameEscape($parts[0]);
$url_hash = Auth_OpenID_FileStore::_safe64($server_url);
if ($handle) {
$handle_hash = Auth_OpenID_FileStore::_safe64($handle);
} else {
$handle_hash = '';
}
$filename = sprintf('%s-%s-%s-%s', $proto, $domain, $url_hash,
$handle_hash);
return $this->association_dir. DIRECTORY_SEPARATOR . $filename;
}
/**
* Store an association in the association directory.
*/
function storeAssociation($server_url, $association)
{
if (!$this->active) {
trigger_error("FileStore no longer active", E_USER_ERROR);
return false;
}
$association_s = $association->serialize();
$filename = $this->getAssociationFilename($server_url,
$association->handle);
list($tmp_file, $tmp) = $this->_mktemp();
if (!$tmp_file) {
trigger_error("_mktemp didn't return a valid file descriptor",
E_USER_WARNING);
return false;
}
fwrite($tmp_file, $association_s);
fflush($tmp_file);
fclose($tmp_file);
if (@rename($tmp, $filename)) {
return true;
} else {
// In case we are running on Windows, try unlinking the
// file in case it exists.
@unlink($filename);
// Now the target should not exist. Try renaming again,
// giving up if it fails.
if (@rename($tmp, $filename)) {
return true;
}
}
// If there was an error, don't leave the temporary file
// around.
Auth_OpenID_FileStore::_removeIfPresent($tmp);
return false;
}
/**
* Retrieve an association. If no handle is specified, return the
* association with the most recent issue time.
*
* @return mixed $association
*/
function getAssociation($server_url, $handle = null)
{
if (!$this->active) {
trigger_error("FileStore no longer active", E_USER_ERROR);
return null;
}
if ($handle === null) {
$handle = '';
}
// The filename with the empty handle is a prefix of all other
// associations for the given server URL.
$filename = $this->getAssociationFilename($server_url, $handle);
if ($handle) {
return $this->_getAssociation($filename);
} else {
$association_files =
Auth_OpenID_FileStore::_listdir($this->association_dir);
$matching_files = array();
// strip off the path to do the comparison
$name = basename($filename);
foreach ($association_files as $association_file) {
$base = basename($association_file);
if (strpos($base, $name) === 0) {
$matching_files[] = $association_file;
}
}
$matching_associations = array();
// read the matching files and sort by time issued
foreach ($matching_files as $full_name) {
$association = $this->_getAssociation($full_name);
if ($association !== null) {
$matching_associations[] = array($association->issued,
$association);
}
}
$issued = array();
$assocs = array();
foreach ($matching_associations as $key => $assoc) {
$issued[$key] = $assoc[0];
$assocs[$key] = $assoc[1];
}
array_multisort($issued, SORT_DESC, $assocs, SORT_DESC,
$matching_associations);
// return the most recently issued one.
if ($matching_associations) {
list($issued, $assoc) = $matching_associations[0];
return $assoc;
} else {
return null;
}
}
}
/**
* @access private
*/
function _getAssociation($filename)
{
if (!$this->active) {
trigger_error("FileStore no longer active", E_USER_ERROR);
return null;
}
if (file_exists($filename) !== true) {
return null;
}
$assoc_file = @fopen($filename, 'rb');
if ($assoc_file === false) {
return null;
}
$filesize = filesize($filename);
if ($filesize === false || $filesize <= 0) {
return null;
}
$assoc_s = fread($assoc_file, $filesize);
fclose($assoc_file);
if (!$assoc_s) {
return null;
}
$association =
Auth_OpenID_Association::deserialize('Auth_OpenID_Association',
$assoc_s);
if (!$association) {
Auth_OpenID_FileStore::_removeIfPresent($filename);
return null;
}
if ($association->getExpiresIn() == 0) {
Auth_OpenID_FileStore::_removeIfPresent($filename);
return null;
} else {
return $association;
}
}
/**
* Remove an association if it exists. Do nothing if it does not.
*
* @return bool $success
*/
function removeAssociation($server_url, $handle)
{
if (!$this->active) {
trigger_error("FileStore no longer active", E_USER_ERROR);
return null;
}
$assoc = $this->getAssociation($server_url, $handle);
if ($assoc === null) {
return false;
} else {
$filename = $this->getAssociationFilename($server_url, $handle);
return Auth_OpenID_FileStore::_removeIfPresent($filename);
}
}
/**
* Return whether this nonce is present. As a side effect, mark it
* as no longer present.
*
* @return bool $present
*/
function useNonce($server_url, $timestamp, $salt)
{
global $Auth_OpenID_SKEW;
if (!$this->active) {
trigger_error("FileStore no longer active", E_USER_ERROR);
return null;
}
if ( abs($timestamp - time()) > $Auth_OpenID_SKEW ) {
return false;
}
if ($server_url) {
list($proto, $rest) = explode('://', $server_url, 2);
} else {
$proto = '';
$rest = '';
}
$parts = explode('/', $rest, 2);
$domain = $this->_filenameEscape($parts[0]);
$url_hash = $this->_safe64($server_url);
$salt_hash = $this->_safe64($salt);
$filename = sprintf('%08x-%s-%s-%s-%s', $timestamp, $proto,
$domain, $url_hash, $salt_hash);
$filename = $this->nonce_dir . DIRECTORY_SEPARATOR . $filename;
$result = @fopen($filename, 'x');
if ($result === false) {
return false;
} else {
fclose($result);
return true;
}
}
/**
* Remove expired entries from the database. This is potentially
* expensive, so only run when it is acceptable to take time.
*
* @access private
*/
function _allAssocs()
{
$all_associations = array();
$association_filenames =
Auth_OpenID_FileStore::_listdir($this->association_dir);
foreach ($association_filenames as $association_filename) {
$association_file = fopen($association_filename, 'rb');
if ($association_file !== false) {
$assoc_s = fread($association_file,
filesize($association_filename));
fclose($association_file);
// Remove expired or corrupted associations
$association =
Auth_OpenID_Association::deserialize(
'Auth_OpenID_Association', $assoc_s);
if ($association === null) {
Auth_OpenID_FileStore::_removeIfPresent(
$association_filename);
} else {
if ($association->getExpiresIn() == 0) {
$all_associations[] = array($association_filename,
$association);
}
}
}
}
return $all_associations;
}
function clean()
{
if (!$this->active) {
trigger_error("FileStore no longer active", E_USER_ERROR);
return null;
}
$nonces = Auth_OpenID_FileStore::_listdir($this->nonce_dir);
$now = time();
// Check all nonces for expiry
foreach ($nonces as $nonce) {
if (!Auth_OpenID_checkTimestamp($nonce, $now)) {
$filename = $this->nonce_dir . DIRECTORY_SEPARATOR . $nonce;
Auth_OpenID_FileStore::_removeIfPresent($filename);
}
}
foreach ($this->_allAssocs() as $pair) {
list($assoc_filename, $assoc) = $pair;
if ($assoc->getExpiresIn() == 0) {
Auth_OpenID_FileStore::_removeIfPresent($assoc_filename);
}
}
}
/**
* @access private
*/
function _rmtree($dir)
{
if ($dir[strlen($dir) - 1] != DIRECTORY_SEPARATOR) {
$dir .= DIRECTORY_SEPARATOR;
}
if ($handle = opendir($dir)) {
while (false !== ($item = readdir($handle))) {
if (!in_array($item, array('.', '..'))) {
if (is_dir($dir . $item)) {
if (!Auth_OpenID_FileStore::_rmtree($dir . $item)) {
return false;
}
} else if (is_file($dir . $item)) {
if (!unlink($dir . $item)) {
return false;
}
}
}
}
closedir($handle);
if (!@rmdir($dir)) {
return false;
}
return true;
} else {
// Couldn't open directory.
return false;
}
}
/**
* @access private
*/
function _mkstemp($dir)
{
foreach (range(0, 4) as $i) {
$name = tempnam($dir, "php_openid_filestore_");
if ($name !== false) {
return $name;
}
}
return false;
}
/**
* @access private
*/
static function _mkdtemp($dir)
{
foreach (range(0, 4) as $i) {
$name = $dir . strval(DIRECTORY_SEPARATOR) . strval(getmypid()) .
"-" . strval(rand(1, time()));
if (!mkdir($name, 0700)) {
return false;
} else {
return $name;
}
}
return false;
}
/**
* @access private
*/
function _listdir($dir)
{
$handle = opendir($dir);
$files = array();
while (false !== ($filename = readdir($handle))) {
if (!in_array($filename, array('.', '..'))) {
$files[] = $dir . DIRECTORY_SEPARATOR . $filename;
}
}
return $files;
}
/**
* @access private
*/
function _isFilenameSafe($char)
{
$_Auth_OpenID_filename_allowed = Auth_OpenID_letters .
Auth_OpenID_digits . ".";
return (strpos($_Auth_OpenID_filename_allowed, $char) !== false);
}
/**
* @access private
*/
function _safe64($str)
{
$h64 = base64_encode(Auth_OpenID_SHA1($str));
$h64 = str_replace('+', '_', $h64);
$h64 = str_replace('/', '.', $h64);
$h64 = str_replace('=', '', $h64);
return $h64;
}
/**
* @access private
*/
function _filenameEscape($str)
{
$filename = "";
$b = Auth_OpenID::toBytes($str);
for ($i = 0; $i < count($b); $i++) {
$c = $b[$i];
if (Auth_OpenID_FileStore::_isFilenameSafe($c)) {
$filename .= $c;
} else {
$filename .= sprintf("_%02X", ord($c));
}
}
return $filename;
}
/**
* Attempt to remove a file, returning whether the file existed at
* the time of the call.
*
* @access private
* @return bool $result True if the file was present, false if not.
*/
function _removeIfPresent($filename)
{
return @unlink($filename);
}
function cleanupAssociations()
{
$removed = 0;
foreach ($this->_allAssocs() as $pair) {
list($assoc_filename, $assoc) = $pair;
if ($assoc->getExpiresIn() == 0) {
$this->_removeIfPresent($assoc_filename);
$removed += 1;
}
}
return $removed;
}
}

View file

@ -1,105 +0,0 @@
<?php
/**
* This is the HMACSHA1 implementation for the OpenID library.
*
* PHP versions 4 and 5
*
* LICENSE: See the COPYING file included in this distribution.
*
* @access private
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005-2008 Janrain, Inc.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
*/
require_once 'Auth/OpenID.php';
/**
* SHA1_BLOCKSIZE is this module's SHA1 blocksize used by the fallback
* implementation.
*/
define('Auth_OpenID_SHA1_BLOCKSIZE', 64);
function Auth_OpenID_SHA1($text)
{
if (function_exists('hash') &&
function_exists('hash_algos') &&
(in_array('sha1', hash_algos()))) {
// PHP 5 case (sometimes): 'hash' available and 'sha1' algo
// supported.
return hash('sha1', $text, true);
} else if (function_exists('sha1')) {
// PHP 4 case: 'sha1' available.
$hex = sha1($text);
$raw = '';
for ($i = 0; $i < 40; $i += 2) {
$hexcode = substr($hex, $i, 2);
$charcode = (int)base_convert($hexcode, 16, 10);
$raw .= chr($charcode);
}
return $raw;
} else {
// Explode.
trigger_error('No SHA1 function found', E_USER_ERROR);
}
}
/**
* Compute an HMAC/SHA1 hash.
*
* @access private
* @param string $key The HMAC key
* @param string $text The message text to hash
* @return string $mac The MAC
*/
function Auth_OpenID_HMACSHA1($key, $text)
{
if (Auth_OpenID::bytes($key) > Auth_OpenID_SHA1_BLOCKSIZE) {
$key = Auth_OpenID_SHA1($key, true);
}
if (function_exists('hash_hmac') &&
function_exists('hash_algos') &&
(in_array('sha1', hash_algos()))) {
return hash_hmac('sha1', $text, $key, true);
}
// Home-made solution
$key = str_pad($key, Auth_OpenID_SHA1_BLOCKSIZE, chr(0x00));
$ipad = str_repeat(chr(0x36), Auth_OpenID_SHA1_BLOCKSIZE);
$opad = str_repeat(chr(0x5c), Auth_OpenID_SHA1_BLOCKSIZE);
$hash1 = Auth_OpenID_SHA1(($key ^ $ipad) . $text, true);
$hmac = Auth_OpenID_SHA1(($key ^ $opad) . $hash1, true);
return $hmac;
}
if (function_exists('hash') &&
function_exists('hash_algos') &&
(in_array('sha256', hash_algos()))) {
function Auth_OpenID_SHA256($text)
{
// PHP 5 case: 'hash' available and 'sha256' algo supported.
return hash('sha256', $text, true);
}
define('Auth_OpenID_SHA256_SUPPORTED', true);
} else {
define('Auth_OpenID_SHA256_SUPPORTED', false);
}
if (function_exists('hash_hmac') &&
function_exists('hash_algos') &&
(in_array('sha256', hash_algos()))) {
function Auth_OpenID_HMACSHA256($key, $text)
{
// Return raw MAC (not hex string).
return hash_hmac('sha256', $text, $key, true);
}
define('Auth_OpenID_HMACSHA256_SUPPORTED', true);
} else {
define('Auth_OpenID_HMACSHA256_SUPPORTED', false);
}

View file

@ -1,196 +0,0 @@
<?php
/**
* This file specifies the interface for PHP OpenID store implementations.
*
* PHP versions 4 and 5
*
* LICENSE: See the COPYING file included in this distribution.
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005-2008 Janrain, Inc.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
*/
/**
* This is the interface for the store objects the OpenID library
* uses. It is a single class that provides all of the persistence
* mechanisms that the OpenID library needs, for both servers and
* consumers. If you want to create an SQL-driven store, please see
* then {@link Auth_OpenID_SQLStore} class.
*
* Change: Version 2.0 removed the storeNonce, getAuthKey, and isDumb
* methods, and changed the behavior of the useNonce method to support
* one-way nonces.
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
*/
class Auth_OpenID_OpenIDStore {
/**
* This method puts an Association object into storage,
* retrievable by server URL and handle.
*
* @param string $server_url The URL of the identity server that
* this association is with. Because of the way the server portion
* of the library uses this interface, don't assume there are any
* limitations on the character set of the input string. In
* particular, expect to see unescaped non-url-safe characters in
* the server_url field.
*
* @param Association $association The Association to store.
*/
function storeAssociation($server_url, $association)
{
trigger_error("Auth_OpenID_OpenIDStore::storeAssociation ".
"not implemented", E_USER_ERROR);
}
/*
* Remove expired nonces from the store.
*
* Discards any nonce from storage that is old enough that its
* timestamp would not pass useNonce().
*
* This method is not called in the normal operation of the
* library. It provides a way for store admins to keep their
* storage from filling up with expired data.
*
* @return the number of nonces expired
*/
function cleanupNonces()
{
trigger_error("Auth_OpenID_OpenIDStore::cleanupNonces ".
"not implemented", E_USER_ERROR);
}
/*
* Remove expired associations from the store.
*
* This method is not called in the normal operation of the
* library. It provides a way for store admins to keep their
* storage from filling up with expired data.
*
* @return the number of associations expired.
*/
function cleanupAssociations()
{
trigger_error("Auth_OpenID_OpenIDStore::cleanupAssociations ".
"not implemented", E_USER_ERROR);
}
/*
* Shortcut for cleanupNonces(), cleanupAssociations().
*
* This method is not called in the normal operation of the
* library. It provides a way for store admins to keep their
* storage from filling up with expired data.
*/
function cleanup()
{
return array($this->cleanupNonces(),
$this->cleanupAssociations());
}
/**
* Report whether this storage supports cleanup
*/
function supportsCleanup()
{
return true;
}
/**
* This method returns an Association object from storage that
* matches the server URL and, if specified, handle. It returns
* null if no such association is found or if the matching
* association is expired.
*
* If no handle is specified, the store may return any association
* which matches the server URL. If multiple associations are
* valid, the recommended return value for this method is the one
* most recently issued.
*
* This method is allowed (and encouraged) to garbage collect
* expired associations when found. This method must not return
* expired associations.
*
* @param string $server_url The URL of the identity server to get
* the association for. Because of the way the server portion of
* the library uses this interface, don't assume there are any
* limitations on the character set of the input string. In
* particular, expect to see unescaped non-url-safe characters in
* the server_url field.
*
* @param mixed $handle This optional parameter is the handle of
* the specific association to get. If no specific handle is
* provided, any valid association matching the server URL is
* returned.
*
* @return Association The Association for the given identity
* server.
*/
function getAssociation($server_url, $handle = null)
{
trigger_error("Auth_OpenID_OpenIDStore::getAssociation ".
"not implemented", E_USER_ERROR);
}
/**
* This method removes the matching association if it's found, and
* returns whether the association was removed or not.
*
* @param string $server_url The URL of the identity server the
* association to remove belongs to. Because of the way the server
* portion of the library uses this interface, don't assume there
* are any limitations on the character set of the input
* string. In particular, expect to see unescaped non-url-safe
* characters in the server_url field.
*
* @param string $handle This is the handle of the association to
* remove. If there isn't an association found that matches both
* the given URL and handle, then there was no matching handle
* found.
*
* @return mixed Returns whether or not the given association existed.
*/
function removeAssociation($server_url, $handle)
{
trigger_error("Auth_OpenID_OpenIDStore::removeAssociation ".
"not implemented", E_USER_ERROR);
}
/**
* Called when using a nonce.
*
* This method should return C{True} if the nonce has not been
* used before, and store it for a while to make sure nobody
* tries to use the same value again. If the nonce has already
* been used, return C{False}.
*
* Change: In earlier versions, round-trip nonces were used and a
* nonce was only valid if it had been previously stored with
* storeNonce. Version 2.0 uses one-way nonces, requiring a
* different implementation here that does not depend on a
* storeNonce call. (storeNonce is no longer part of the
* interface.
*
* @param string $nonce The nonce to use.
*
* @return bool Whether or not the nonce was valid.
*/
function useNonce($server_url, $timestamp, $salt)
{
trigger_error("Auth_OpenID_OpenIDStore::useNonce ".
"not implemented", E_USER_ERROR);
}
/**
* Removes all entries from the store; implementation is optional.
*/
function reset()
{
}
}

View file

@ -1,111 +0,0 @@
<?php
/**
* OpenID protocol key-value/comma-newline format parsing and
* serialization
*
* PHP versions 4 and 5
*
* LICENSE: See the COPYING file included in this distribution.
*
* @access private
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005-2008 Janrain, Inc.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
*/
/**
* Container for key-value/comma-newline OpenID format and parsing
*/
class Auth_OpenID_KVForm {
/**
* Convert an OpenID colon/newline separated string into an
* associative array
*
* @static
* @access private
*/
static function toArray($kvs, $strict=false)
{
$lines = explode("\n", $kvs);
$last = array_pop($lines);
if ($last !== '') {
array_push($lines, $last);
if ($strict) {
return false;
}
}
$values = array();
for ($lineno = 0; $lineno < count($lines); $lineno++) {
$line = $lines[$lineno];
$kv = explode(':', $line, 2);
if (count($kv) != 2) {
if ($strict) {
return false;
}
continue;
}
$key = $kv[0];
$tkey = trim($key);
if ($tkey != $key) {
if ($strict) {
return false;
}
}
$value = $kv[1];
$tval = trim($value);
if ($tval != $value) {
if ($strict) {
return false;
}
}
$values[$tkey] = $tval;
}
return $values;
}
/**
* Convert an array into an OpenID colon/newline separated string
*
* @static
* @access private
*/
static function fromArray($values)
{
if ($values === null) {
return null;
}
ksort($values);
$serialized = '';
foreach ($values as $key => $value) {
if (is_array($value)) {
list($key, $value) = array($value[0], $value[1]);
}
if (strpos($key, ':') !== false) {
return null;
}
if (strpos($key, "\n") !== false) {
return null;
}
if (strpos($value, "\n") !== false) {
return null;
}
$serialized .= "$key:$value\n";
}
return $serialized;
}
}

View file

@ -1,413 +0,0 @@
<?php
/**
* SQL-backed OpenID stores for use with PEAR::MDB2.
*
* PHP versions 4 and 5
*
* LICENSE: See the COPYING file included in this distribution.
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
*/
require_once 'MDB2.php';
/**
* @access private
*/
require_once 'Auth/OpenID/Interface.php';
/**
* @access private
*/
require_once 'Auth/OpenID.php';
/**
* @access private
*/
require_once 'Auth/OpenID/Nonce.php';
/**
* This store uses a PEAR::MDB2 connection to store persistence
* information.
*
* The table names used are determined by the class variables
* associations_table_name and nonces_table_name. To change the name
* of the tables used, pass new table names into the constructor.
*
* To create the tables with the proper schema, see the createTables
* method.
*
* @package OpenID
*/
class Auth_OpenID_MDB2Store extends Auth_OpenID_OpenIDStore {
/**
* This creates a new MDB2Store instance. It requires an
* established database connection be given to it, and it allows
* overriding the default table names.
*
* @param connection $connection This must be an established
* connection to a database of the correct type for the SQLStore
* subclass you're using. This must be a PEAR::MDB2 connection
* handle.
*
* @param associations_table: This is an optional parameter to
* specify the name of the table used for storing associations.
* The default value is 'oid_associations'.
*
* @param nonces_table: This is an optional parameter to specify
* the name of the table used for storing nonces. The default
* value is 'oid_nonces'.
*/
function Auth_OpenID_MDB2Store($connection,
$associations_table = null,
$nonces_table = null)
{
$this->associations_table_name = "oid_associations";
$this->nonces_table_name = "oid_nonces";
// Check the connection object type to be sure it's a PEAR
// database connection.
if (!is_object($connection) ||
!is_subclass_of($connection, 'mdb2_driver_common')) {
trigger_error("Auth_OpenID_MDB2Store expected PEAR connection " .
"object (got ".get_class($connection).")",
E_USER_ERROR);
return;
}
$this->connection = $connection;
// Be sure to set the fetch mode so the results are keyed on
// column name instead of column index.
$this->connection->setFetchMode(MDB2_FETCHMODE_ASSOC);
if (@PEAR::isError($this->connection->loadModule('Extended'))) {
trigger_error("Unable to load MDB2_Extended module", E_USER_ERROR);
return;
}
if ($associations_table) {
$this->associations_table_name = $associations_table;
}
if ($nonces_table) {
$this->nonces_table_name = $nonces_table;
}
$this->max_nonce_age = 6 * 60 * 60;
}
function tableExists($table_name)
{
return !@PEAR::isError($this->connection->query(
sprintf("SELECT * FROM %s LIMIT 0",
$table_name)));
}
function createTables()
{
$n = $this->create_nonce_table();
$a = $this->create_assoc_table();
if (!$n || !$a) {
return false;
}
return true;
}
function create_nonce_table()
{
if (!$this->tableExists($this->nonces_table_name)) {
switch ($this->connection->phptype) {
case "mysql":
case "mysqli":
// Custom SQL for MySQL to use InnoDB and variable-
// length keys
$r = $this->connection->exec(
sprintf("CREATE TABLE %s (\n".
" server_url VARCHAR(2047) NOT NULL DEFAULT '',\n".
" timestamp INTEGER NOT NULL,\n".
" salt CHAR(40) NOT NULL,\n".
" UNIQUE (server_url(255), timestamp, salt)\n".
") TYPE=InnoDB",
$this->nonces_table_name));
if (@PEAR::isError($r)) {
return false;
}
break;
default:
if (@PEAR::isError(
$this->connection->loadModule('Manager'))) {
return false;
}
$fields = array(
"server_url" => array(
"type" => "text",
"length" => 2047,
"notnull" => true
),
"timestamp" => array(
"type" => "integer",
"notnull" => true
),
"salt" => array(
"type" => "text",
"length" => 40,
"fixed" => true,
"notnull" => true
)
);
$constraint = array(
"unique" => 1,
"fields" => array(
"server_url" => true,
"timestamp" => true,
"salt" => true
)
);
$r = $this->connection->createTable($this->nonces_table_name,
$fields);
if (@PEAR::isError($r)) {
return false;
}
$r = $this->connection->createConstraint(
$this->nonces_table_name,
$this->nonces_table_name . "_constraint",
$constraint);
if (@PEAR::isError($r)) {
return false;
}
break;
}
}
return true;
}
function create_assoc_table()
{
if (!$this->tableExists($this->associations_table_name)) {
switch ($this->connection->phptype) {
case "mysql":
case "mysqli":
// Custom SQL for MySQL to use InnoDB and variable-
// length keys
$r = $this->connection->exec(
sprintf("CREATE TABLE %s(\n".
" server_url VARCHAR(2047) NOT NULL DEFAULT '',\n".
" handle VARCHAR(255) NOT NULL,\n".
" secret BLOB NOT NULL,\n".
" issued INTEGER NOT NULL,\n".
" lifetime INTEGER NOT NULL,\n".
" assoc_type VARCHAR(64) NOT NULL,\n".
" PRIMARY KEY (server_url(255), handle)\n".
") TYPE=InnoDB",
$this->associations_table_name));
if (@PEAR::isError($r)) {
return false;
}
break;
default:
if (@PEAR::isError(
$this->connection->loadModule('Manager'))) {
return false;
}
$fields = array(
"server_url" => array(
"type" => "text",
"length" => 2047,
"notnull" => true
),
"handle" => array(
"type" => "text",
"length" => 255,
"notnull" => true
),
"secret" => array(
"type" => "blob",
"length" => "255",
"notnull" => true
),
"issued" => array(
"type" => "integer",
"notnull" => true
),
"lifetime" => array(
"type" => "integer",
"notnull" => true
),
"assoc_type" => array(
"type" => "text",
"length" => 64,
"notnull" => true
)
);
$options = array(
"primary" => array(
"server_url" => true,
"handle" => true
)
);
$r = $this->connection->createTable(
$this->associations_table_name,
$fields,
$options);
if (@PEAR::isError($r)) {
return false;
}
break;
}
}
return true;
}
function storeAssociation($server_url, $association)
{
$fields = array(
"server_url" => array(
"value" => $server_url,
"key" => true
),
"handle" => array(
"value" => $association->handle,
"key" => true
),
"secret" => array(
"value" => $association->secret,
"type" => "blob"
),
"issued" => array(
"value" => $association->issued
),
"lifetime" => array(
"value" => $association->lifetime
),
"assoc_type" => array(
"value" => $association->assoc_type
)
);
return !@PEAR::isError($this->connection->replace(
$this->associations_table_name,
$fields));
}
function cleanupNonces()
{
global $Auth_OpenID_SKEW;
$v = time() - $Auth_OpenID_SKEW;
return $this->connection->exec(
sprintf("DELETE FROM %s WHERE timestamp < %d",
$this->nonces_table_name, $v));
}
function cleanupAssociations()
{
return $this->connection->exec(
sprintf("DELETE FROM %s WHERE issued + lifetime < %d",
$this->associations_table_name, time()));
}
function getAssociation($server_url, $handle = null)
{
$sql = "";
$params = null;
$types = array(
"text",
"blob",
"integer",
"integer",
"text"
);
if ($handle !== null) {
$sql = sprintf("SELECT handle, secret, issued, lifetime, assoc_type " .
"FROM %s WHERE server_url = ? AND handle = ?",
$this->associations_table_name);
$params = array($server_url, $handle);
} else {
$sql = sprintf("SELECT handle, secret, issued, lifetime, assoc_type " .
"FROM %s WHERE server_url = ? ORDER BY issued DESC",
$this->associations_table_name);
$params = array($server_url);
}
$assoc = $this->connection->getRow($sql, $types, $params);
if (!$assoc || @PEAR::isError($assoc)) {
return null;
} else {
$association = new Auth_OpenID_Association($assoc['handle'],
stream_get_contents(
$assoc['secret']),
$assoc['issued'],
$assoc['lifetime'],
$assoc['assoc_type']);
fclose($assoc['secret']);
return $association;
}
}
function removeAssociation($server_url, $handle)
{
$r = $this->connection->execParam(
sprintf("DELETE FROM %s WHERE server_url = ? AND handle = ?",
$this->associations_table_name),
array($server_url, $handle));
if (@PEAR::isError($r) || $r == 0) {
return false;
}
return true;
}
function useNonce($server_url, $timestamp, $salt)
{
global $Auth_OpenID_SKEW;
if (abs($timestamp - time()) > $Auth_OpenID_SKEW ) {
return false;
}
$fields = array(
"timestamp" => $timestamp,
"salt" => $salt
);
if (!empty($server_url)) {
$fields["server_url"] = $server_url;
}
$r = $this->connection->autoExecute(
$this->nonces_table_name,
$fields,
MDB2_AUTOQUERY_INSERT);
if (@PEAR::isError($r)) {
return false;
}
return true;
}
/**
* Resets the store by removing all records from the store's
* tables.
*/
function reset()
{
$this->connection->query(sprintf("DELETE FROM %s",
$this->associations_table_name));
$this->connection->query(sprintf("DELETE FROM %s",
$this->nonces_table_name));
}
}
?>

View file

@ -1,207 +0,0 @@
<?php
/**
* This file supplies a memcached store backend for OpenID servers and
* consumers.
*
* PHP versions 4 and 5
*
* LICENSE: See the COPYING file included in this distribution.
*
* @package OpenID
* @author Artemy Tregubenko <me@arty.name>
* @copyright 2008 JanRain, Inc.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
* Contributed by Open Web Technologies <http://openwebtech.ru/>
*/
/**
* Import the interface for creating a new store class.
*/
require_once 'Auth/OpenID/Interface.php';
/**
* This is a memcached-based store for OpenID associations and
* nonces.
*
* As memcache has limit of 250 chars for key length,
* server_url, handle and salt are hashed with sha1().
*
* Most of the methods of this class are implementation details.
* People wishing to just use this store need only pay attention to
* the constructor.
*
* @package OpenID
*/
class Auth_OpenID_MemcachedStore extends Auth_OpenID_OpenIDStore {
/**
* Initializes a new {@link Auth_OpenID_MemcachedStore} instance.
* Just saves memcached object as property.
*
* @param resource connection Memcache connection resourse
*/
function Auth_OpenID_MemcachedStore($connection, $compress = false)
{
$this->connection = $connection;
$this->compress = $compress ? MEMCACHE_COMPRESSED : 0;
}
/**
* Store association until its expiration time in memcached.
* Overwrites any existing association with same server_url and
* handle. Handles list of associations for every server.
*/
function storeAssociation($server_url, $association)
{
// create memcached keys for association itself
// and list of associations for this server
$associationKey = $this->associationKey($server_url,
$association->handle);
$serverKey = $this->associationServerKey($server_url);
// get list of associations
$serverAssociations = $this->connection->get($serverKey);
// if no such list, initialize it with empty array
if (!$serverAssociations) {
$serverAssociations = array();
}
// and store given association key in it
$serverAssociations[$association->issued] = $associationKey;
// save associations' keys list
$this->connection->set(
$serverKey,
$serverAssociations,
$this->compress
);
// save association itself
$this->connection->set(
$associationKey,
$association,
$this->compress,
$association->issued + $association->lifetime);
}
/**
* Read association from memcached. If no handle given
* and multiple associations found, returns latest issued
*/
function getAssociation($server_url, $handle = null)
{
// simple case: handle given
if ($handle !== null) {
// get association, return null if failed
$association = $this->connection->get(
$this->associationKey($server_url, $handle));
return $association ? $association : null;
}
// no handle given, working with list
// create key for list of associations
$serverKey = $this->associationServerKey($server_url);
// get list of associations
$serverAssociations = $this->connection->get($serverKey);
// return null if failed or got empty list
if (!$serverAssociations) {
return null;
}
// get key of most recently issued association
$keys = array_keys($serverAssociations);
sort($keys);
$lastKey = $serverAssociations[array_pop($keys)];
// get association, return null if failed
$association = $this->connection->get($lastKey);
return $association ? $association : null;
}
/**
* Immediately delete association from memcache.
*/
function removeAssociation($server_url, $handle)
{
// create memcached keys for association itself
// and list of associations for this server
$serverKey = $this->associationServerKey($server_url);
$associationKey = $this->associationKey($server_url,
$handle);
// get list of associations
$serverAssociations = $this->connection->get($serverKey);
// return null if failed or got empty list
if (!$serverAssociations) {
return false;
}
// ensure that given association key exists in list
$serverAssociations = array_flip($serverAssociations);
if (!array_key_exists($associationKey, $serverAssociations)) {
return false;
}
// remove given association key from list
unset($serverAssociations[$associationKey]);
$serverAssociations = array_flip($serverAssociations);
// save updated list
$this->connection->set(
$serverKey,
$serverAssociations,
$this->compress
);
// delete association
return $this->connection->delete($associationKey);
}
/**
* Create nonce for server and salt, expiring after
* $Auth_OpenID_SKEW seconds.
*/
function useNonce($server_url, $timestamp, $salt)
{
global $Auth_OpenID_SKEW;
// save one request to memcache when nonce obviously expired
if (abs($timestamp - time()) > $Auth_OpenID_SKEW) {
return false;
}
// returns false when nonce already exists
// otherwise adds nonce
return $this->connection->add(
'openid_nonce_' . sha1($server_url) . '_' . sha1($salt),
1, // any value here
$this->compress,
$Auth_OpenID_SKEW);
}
/**
* Memcache key is prefixed with 'openid_association_' string.
*/
function associationKey($server_url, $handle = null)
{
return 'openid_association_' . sha1($server_url) . '_' . sha1($handle);
}
/**
* Memcache key is prefixed with 'openid_association_' string.
*/
function associationServerKey($server_url)
{
return 'openid_association_server_' . sha1($server_url);
}
/**
* Report that this storage doesn't support cleanup
*/
function supportsCleanup()
{
return false;
}
}

View file

@ -1,920 +0,0 @@
<?php
/**
* Extension argument processing code
*
* @package OpenID
*/
/**
* Import tools needed to deal with messages.
*/
require_once 'Auth/OpenID.php';
require_once 'Auth/OpenID/KVForm.php';
require_once 'Auth/Yadis/XML.php';
require_once 'Auth/OpenID/Consumer.php'; // For Auth_OpenID_FailureResponse
// This doesn't REALLY belong here, but where is better?
define('Auth_OpenID_IDENTIFIER_SELECT',
"http://specs.openid.net/auth/2.0/identifier_select");
// URI for Simple Registration extension, the only commonly deployed
// OpenID 1.x extension, and so a special case
define('Auth_OpenID_SREG_URI', 'http://openid.net/sreg/1.0');
// The OpenID 1.X namespace URI
define('Auth_OpenID_OPENID1_NS', 'http://openid.net/signon/1.0');
define('Auth_OpenID_THE_OTHER_OPENID1_NS', 'http://openid.net/signon/1.1');
function Auth_OpenID_isOpenID1($ns)
{
return ($ns == Auth_OpenID_THE_OTHER_OPENID1_NS) ||
($ns == Auth_OpenID_OPENID1_NS);
}
// The OpenID 2.0 namespace URI
define('Auth_OpenID_OPENID2_NS', 'http://specs.openid.net/auth/2.0');
// The namespace consisting of pairs with keys that are prefixed with
// "openid." but not in another namespace.
define('Auth_OpenID_NULL_NAMESPACE', 'Null namespace');
// The null namespace, when it is an allowed OpenID namespace
define('Auth_OpenID_OPENID_NS', 'OpenID namespace');
// The top-level namespace, excluding all pairs with keys that start
// with "openid."
define('Auth_OpenID_BARE_NS', 'Bare namespace');
// Sentinel for Message implementation to indicate that getArg should
// return null instead of returning a default.
define('Auth_OpenID_NO_DEFAULT', 'NO DEFAULT ALLOWED');
// Limit, in bytes, of identity provider and return_to URLs, including
// response payload. See OpenID 1.1 specification, Appendix D.
define('Auth_OpenID_OPENID1_URL_LIMIT', 2047);
// All OpenID protocol fields. Used to check namespace aliases.
global $Auth_OpenID_OPENID_PROTOCOL_FIELDS;
$Auth_OpenID_OPENID_PROTOCOL_FIELDS = array(
'ns', 'mode', 'error', 'return_to', 'contact', 'reference',
'signed', 'assoc_type', 'session_type', 'dh_modulus', 'dh_gen',
'dh_consumer_public', 'claimed_id', 'identity', 'realm',
'invalidate_handle', 'op_endpoint', 'response_nonce', 'sig',
'assoc_handle', 'trust_root', 'openid');
// Global namespace / alias registration map. See
// Auth_OpenID_registerNamespaceAlias.
global $Auth_OpenID_registered_aliases;
$Auth_OpenID_registered_aliases = array();
/**
* Registers a (namespace URI, alias) mapping in a global namespace
* alias map. Raises NamespaceAliasRegistrationError if either the
* namespace URI or alias has already been registered with a different
* value. This function is required if you want to use a namespace
* with an OpenID 1 message.
*/
function Auth_OpenID_registerNamespaceAlias($namespace_uri, $alias)
{
global $Auth_OpenID_registered_aliases;
if (Auth_OpenID::arrayGet($Auth_OpenID_registered_aliases,
$alias) == $namespace_uri) {
return true;
}
if (in_array($namespace_uri,
array_values($Auth_OpenID_registered_aliases))) {
return false;
}
if (in_array($alias, array_keys($Auth_OpenID_registered_aliases))) {
return false;
}
$Auth_OpenID_registered_aliases[$alias] = $namespace_uri;
return true;
}
/**
* Removes a (namespace_uri, alias) registration from the global
* namespace alias map. Returns true if the removal succeeded; false
* if not (if the mapping did not exist).
*/
function Auth_OpenID_removeNamespaceAlias($namespace_uri, $alias)
{
global $Auth_OpenID_registered_aliases;
if (Auth_OpenID::arrayGet($Auth_OpenID_registered_aliases,
$alias) === $namespace_uri) {
unset($Auth_OpenID_registered_aliases[$alias]);
return true;
}
return false;
}
/**
* An Auth_OpenID_Mapping maintains a mapping from arbitrary keys to
* arbitrary values. (This is unlike an ordinary PHP array, whose
* keys may be only simple scalars.)
*
* @package OpenID
*/
class Auth_OpenID_Mapping {
/**
* Initialize a mapping. If $classic_array is specified, its keys
* and values are used to populate the mapping.
*/
function Auth_OpenID_Mapping($classic_array = null)
{
$this->keys = array();
$this->values = array();
if (is_array($classic_array)) {
foreach ($classic_array as $key => $value) {
$this->set($key, $value);
}
}
}
/**
* Returns true if $thing is an Auth_OpenID_Mapping object; false
* if not.
*/
static function isA($thing)
{
return (is_object($thing) &&
strtolower(get_class($thing)) == 'auth_openid_mapping');
}
/**
* Returns an array of the keys in the mapping.
*/
function keys()
{
return $this->keys;
}
/**
* Returns an array of values in the mapping.
*/
function values()
{
return $this->values;
}
/**
* Returns an array of (key, value) pairs in the mapping.
*/
function items()
{
$temp = array();
for ($i = 0; $i < count($this->keys); $i++) {
$temp[] = array($this->keys[$i],
$this->values[$i]);
}
return $temp;
}
/**
* Returns the "length" of the mapping, or the number of keys.
*/
function len()
{
return count($this->keys);
}
/**
* Sets a key-value pair in the mapping. If the key already
* exists, its value is replaced with the new value.
*/
function set($key, $value)
{
$index = array_search($key, $this->keys);
if ($index !== false) {
$this->values[$index] = $value;
} else {
$this->keys[] = $key;
$this->values[] = $value;
}
}
/**
* Gets a specified value from the mapping, associated with the
* specified key. If the key does not exist in the mapping,
* $default is returned instead.
*/
function get($key, $default = null)
{
$index = array_search($key, $this->keys);
if ($index !== false) {
return $this->values[$index];
} else {
return $default;
}
}
/**
* @access private
*/
function _reflow()
{
// PHP is broken yet again. Sort the arrays to remove the
// hole in the numeric indexes that make up the array.
$old_keys = $this->keys;
$old_values = $this->values;
$this->keys = array();
$this->values = array();
foreach ($old_keys as $k) {
$this->keys[] = $k;
}
foreach ($old_values as $v) {
$this->values[] = $v;
}
}
/**
* Deletes a key-value pair from the mapping with the specified
* key.
*/
function del($key)
{
$index = array_search($key, $this->keys);
if ($index !== false) {
unset($this->keys[$index]);
unset($this->values[$index]);
$this->_reflow();
return true;
}
return false;
}
/**
* Returns true if the specified value has a key in the mapping;
* false if not.
*/
function contains($value)
{
return (array_search($value, $this->keys) !== false);
}
}
/**
* Maintains a bijective map between namespace uris and aliases.
*
* @package OpenID
*/
class Auth_OpenID_NamespaceMap {
function Auth_OpenID_NamespaceMap()
{
$this->alias_to_namespace = new Auth_OpenID_Mapping();
$this->namespace_to_alias = new Auth_OpenID_Mapping();
$this->implicit_namespaces = array();
}
function getAlias($namespace_uri)
{
return $this->namespace_to_alias->get($namespace_uri);
}
function getNamespaceURI($alias)
{
return $this->alias_to_namespace->get($alias);
}
function iterNamespaceURIs()
{
// Return an iterator over the namespace URIs
return $this->namespace_to_alias->keys();
}
function iterAliases()
{
// Return an iterator over the aliases"""
return $this->alias_to_namespace->keys();
}
function iteritems()
{
return $this->namespace_to_alias->items();
}
function isImplicit($namespace_uri)
{
return in_array($namespace_uri, $this->implicit_namespaces);
}
function addAlias($namespace_uri, $desired_alias, $implicit=false)
{
// Add an alias from this namespace URI to the desired alias
global $Auth_OpenID_OPENID_PROTOCOL_FIELDS;
// Check that desired_alias is not an openid protocol field as
// per the spec.
if (in_array($desired_alias, $Auth_OpenID_OPENID_PROTOCOL_FIELDS)) {
Auth_OpenID::log("\"%s\" is not an allowed namespace alias",
$desired_alias);
return null;
}
// Check that desired_alias does not contain a period as per
// the spec.
if (strpos($desired_alias, '.') !== false) {
Auth_OpenID::log('"%s" must not contain a dot', $desired_alias);
return null;
}
// Check that there is not a namespace already defined for the
// desired alias
$current_namespace_uri =
$this->alias_to_namespace->get($desired_alias);
if (($current_namespace_uri !== null) &&
($current_namespace_uri != $namespace_uri)) {
Auth_OpenID::log('Cannot map "%s" because previous mapping exists',
$namespace_uri);
return null;
}
// Check that there is not already a (different) alias for
// this namespace URI
$alias = $this->namespace_to_alias->get($namespace_uri);
if (($alias !== null) && ($alias != $desired_alias)) {
Auth_OpenID::log('Cannot map %s to alias %s. ' .
'It is already mapped to alias %s',
$namespace_uri, $desired_alias, $alias);
return null;
}
assert((Auth_OpenID_NULL_NAMESPACE === $desired_alias) ||
is_string($desired_alias));
$this->alias_to_namespace->set($desired_alias, $namespace_uri);
$this->namespace_to_alias->set($namespace_uri, $desired_alias);
if ($implicit) {
array_push($this->implicit_namespaces, $namespace_uri);
}
return $desired_alias;
}
function add($namespace_uri)
{
// Add this namespace URI to the mapping, without caring what
// alias it ends up with
// See if this namespace is already mapped to an alias
$alias = $this->namespace_to_alias->get($namespace_uri);
if ($alias !== null) {
return $alias;
}
// Fall back to generating a numerical alias
$i = 0;
while (1) {
$alias = 'ext' . strval($i);
if ($this->addAlias($namespace_uri, $alias) === null) {
$i += 1;
} else {
return $alias;
}
}
// Should NEVER be reached!
return null;
}
function contains($namespace_uri)
{
return $this->isDefined($namespace_uri);
}
function isDefined($namespace_uri)
{
return $this->namespace_to_alias->contains($namespace_uri);
}
}
/**
* In the implementation of this object, null represents the global
* namespace as well as a namespace with no key.
*
* @package OpenID
*/
class Auth_OpenID_Message {
function Auth_OpenID_Message($openid_namespace = null)
{
// Create an empty Message
$this->allowed_openid_namespaces = array(
Auth_OpenID_OPENID1_NS,
Auth_OpenID_THE_OTHER_OPENID1_NS,
Auth_OpenID_OPENID2_NS);
$this->args = new Auth_OpenID_Mapping();
$this->namespaces = new Auth_OpenID_NamespaceMap();
if ($openid_namespace === null) {
$this->_openid_ns_uri = null;
} else {
$implicit = Auth_OpenID_isOpenID1($openid_namespace);
$this->setOpenIDNamespace($openid_namespace, $implicit);
}
}
function isOpenID1()
{
return Auth_OpenID_isOpenID1($this->getOpenIDNamespace());
}
function isOpenID2()
{
return $this->getOpenIDNamespace() == Auth_OpenID_OPENID2_NS;
}
static function fromPostArgs($args)
{
// Construct a Message containing a set of POST arguments
$obj = new Auth_OpenID_Message();
// Partition into "openid." args and bare args
$openid_args = array();
foreach ($args as $key => $value) {
if (is_array($value)) {
return null;
}
$parts = explode('.', $key, 2);
if (count($parts) == 2) {
list($prefix, $rest) = $parts;
} else {
$prefix = null;
}
if ($prefix != 'openid') {
$obj->args->set(array(Auth_OpenID_BARE_NS, $key), $value);
} else {
$openid_args[$rest] = $value;
}
}
if ($obj->_fromOpenIDArgs($openid_args)) {
return $obj;
} else {
return null;
}
}
static function fromOpenIDArgs($openid_args)
{
// Takes an array.
// Construct a Message from a parsed KVForm message
$obj = new Auth_OpenID_Message();
if ($obj->_fromOpenIDArgs($openid_args)) {
return $obj;
} else {
return null;
}
}
/**
* @access private
*/
function _fromOpenIDArgs($openid_args)
{
global $Auth_OpenID_registered_aliases;
// Takes an Auth_OpenID_Mapping instance OR an array.
if (!Auth_OpenID_Mapping::isA($openid_args)) {
$openid_args = new Auth_OpenID_Mapping($openid_args);
}
$ns_args = array();
// Resolve namespaces
foreach ($openid_args->items() as $pair) {
list($rest, $value) = $pair;
$parts = explode('.', $rest, 2);
if (count($parts) == 2) {
list($ns_alias, $ns_key) = $parts;
} else {
$ns_alias = Auth_OpenID_NULL_NAMESPACE;
$ns_key = $rest;
}
if ($ns_alias == 'ns') {
if ($this->namespaces->addAlias($value, $ns_key) === null) {
return false;
}
} else if (($ns_alias == Auth_OpenID_NULL_NAMESPACE) &&
($ns_key == 'ns')) {
// null namespace
if ($this->setOpenIDNamespace($value, false) === false) {
return false;
}
} else {
$ns_args[] = array($ns_alias, $ns_key, $value);
}
}
if (!$this->getOpenIDNamespace()) {
if ($this->setOpenIDNamespace(Auth_OpenID_OPENID1_NS, true) ===
false) {
return false;
}
}
// Actually put the pairs into the appropriate namespaces
foreach ($ns_args as $triple) {
list($ns_alias, $ns_key, $value) = $triple;
$ns_uri = $this->namespaces->getNamespaceURI($ns_alias);
if ($ns_uri === null) {
$ns_uri = $this->_getDefaultNamespace($ns_alias);
if ($ns_uri === null) {
$ns_uri = Auth_OpenID_OPENID_NS;
$ns_key = sprintf('%s.%s', $ns_alias, $ns_key);
} else {
$this->namespaces->addAlias($ns_uri, $ns_alias, true);
}
}
$this->setArg($ns_uri, $ns_key, $value);
}
return true;
}
function _getDefaultNamespace($mystery_alias)
{
global $Auth_OpenID_registered_aliases;
if ($this->isOpenID1()) {
return @$Auth_OpenID_registered_aliases[$mystery_alias];
}
return null;
}
function setOpenIDNamespace($openid_ns_uri, $implicit)
{
if (!in_array($openid_ns_uri, $this->allowed_openid_namespaces)) {
Auth_OpenID::log('Invalid null namespace: "%s"', $openid_ns_uri);
return false;
}
$succeeded = $this->namespaces->addAlias($openid_ns_uri,
Auth_OpenID_NULL_NAMESPACE,
$implicit);
if ($succeeded === false) {
return false;
}
$this->_openid_ns_uri = $openid_ns_uri;
return true;
}
function getOpenIDNamespace()
{
return $this->_openid_ns_uri;
}
static function fromKVForm($kvform_string)
{
// Create a Message from a KVForm string
return Auth_OpenID_Message::fromOpenIDArgs(
Auth_OpenID_KVForm::toArray($kvform_string));
}
function copy()
{
return $this;
}
function toPostArgs()
{
// Return all arguments with openid. in front of namespaced
// arguments.
$args = array();
// Add namespace definitions to the output
foreach ($this->namespaces->iteritems() as $pair) {
list($ns_uri, $alias) = $pair;
if ($this->namespaces->isImplicit($ns_uri)) {
continue;
}
if ($alias == Auth_OpenID_NULL_NAMESPACE) {
$ns_key = 'openid.ns';
} else {
$ns_key = 'openid.ns.' . $alias;
}
$args[$ns_key] = $ns_uri;
}
foreach ($this->args->items() as $pair) {
list($ns_parts, $value) = $pair;
list($ns_uri, $ns_key) = $ns_parts;
$key = $this->getKey($ns_uri, $ns_key);
$args[$key] = $value;
}
return $args;
}
function toArgs()
{
// Return all namespaced arguments, failing if any
// non-namespaced arguments exist.
$post_args = $this->toPostArgs();
$kvargs = array();
foreach ($post_args as $k => $v) {
if (strpos($k, 'openid.') !== 0) {
// raise ValueError(
// 'This message can only be encoded as a POST, because it '
// 'contains arguments that are not prefixed with "openid."')
return null;
} else {
$kvargs[substr($k, 7)] = $v;
}
}
return $kvargs;
}
function toFormMarkup($action_url, $form_tag_attrs = null,
$submit_text = "Continue")
{
$form = "<form accept-charset=\"UTF-8\" ".
"enctype=\"application/x-www-form-urlencoded\"";
if (!$form_tag_attrs) {
$form_tag_attrs = array();
}
$form_tag_attrs['action'] = $action_url;
$form_tag_attrs['method'] = 'post';
unset($form_tag_attrs['enctype']);
unset($form_tag_attrs['accept-charset']);
if ($form_tag_attrs) {
foreach ($form_tag_attrs as $name => $attr) {
$form .= sprintf(" %s=\"%s\"", $name, htmlspecialchars($attr));
}
}
$form .= ">\n";
foreach ($this->toPostArgs() as $name => $value) {
$form .= sprintf(
"<input type=\"hidden\" name=\"%s\" value=\"%s\" />\n",
htmlspecialchars($name), htmlspecialchars($value));
}
$form .= sprintf("<input type=\"submit\" value=\"%s\" />\n",
htmlspecialchars($submit_text));
$form .= "</form>\n";
return $form;
}
function toURL($base_url)
{
// Generate a GET URL with the parameters in this message
// attached as query parameters.
return Auth_OpenID::appendArgs($base_url, $this->toPostArgs());
}
function toKVForm()
{
// Generate a KVForm string that contains the parameters in
// this message. This will fail if the message contains
// arguments outside of the 'openid.' prefix.
return Auth_OpenID_KVForm::fromArray($this->toArgs());
}
function toURLEncoded()
{
// Generate an x-www-urlencoded string
$args = array();
foreach ($this->toPostArgs() as $k => $v) {
$args[] = array($k, $v);
}
sort($args);
return Auth_OpenID::httpBuildQuery($args);
}
/**
* @access private
*/
function _fixNS($namespace)
{
// Convert an input value into the internally used values of
// this object
if ($namespace == Auth_OpenID_OPENID_NS) {
if ($this->_openid_ns_uri === null) {
return new Auth_OpenID_FailureResponse(null,
'OpenID namespace not set');
} else {
$namespace = $this->_openid_ns_uri;
}
}
if (($namespace != Auth_OpenID_BARE_NS) &&
(!is_string($namespace))) {
//TypeError
$err_msg = sprintf("Namespace must be Auth_OpenID_BARE_NS, ".
"Auth_OpenID_OPENID_NS or a string. got %s",
print_r($namespace, true));
return new Auth_OpenID_FailureResponse(null, $err_msg);
}
if (($namespace != Auth_OpenID_BARE_NS) &&
(strpos($namespace, ':') === false)) {
// fmt = 'OpenID 2.0 namespace identifiers SHOULD be URIs. Got %r'
// warnings.warn(fmt % (namespace,), DeprecationWarning)
if ($namespace == 'sreg') {
// fmt = 'Using %r instead of "sreg" as namespace'
// warnings.warn(fmt % (SREG_URI,), DeprecationWarning,)
return Auth_OpenID_SREG_URI;
}
}
return $namespace;
}
function hasKey($namespace, $ns_key)
{
$namespace = $this->_fixNS($namespace);
if (Auth_OpenID::isFailure($namespace)) {
// XXX log me
return false;
} else {
return $this->args->contains(array($namespace, $ns_key));
}
}
function getKey($namespace, $ns_key)
{
// Get the key for a particular namespaced argument
$namespace = $this->_fixNS($namespace);
if (Auth_OpenID::isFailure($namespace)) {
return $namespace;
}
if ($namespace == Auth_OpenID_BARE_NS) {
return $ns_key;
}
$ns_alias = $this->namespaces->getAlias($namespace);
// No alias is defined, so no key can exist
if ($ns_alias === null) {
return null;
}
if ($ns_alias == Auth_OpenID_NULL_NAMESPACE) {
$tail = $ns_key;
} else {
$tail = sprintf('%s.%s', $ns_alias, $ns_key);
}
return 'openid.' . $tail;
}
function getArg($namespace, $key, $default = null)
{
// Get a value for a namespaced key.
$namespace = $this->_fixNS($namespace);
if (Auth_OpenID::isFailure($namespace)) {
return $namespace;
} else {
if ((!$this->args->contains(array($namespace, $key))) &&
($default == Auth_OpenID_NO_DEFAULT)) {
$err_msg = sprintf("Namespace %s missing required field %s",
$namespace, $key);
return new Auth_OpenID_FailureResponse(null, $err_msg);
} else {
return $this->args->get(array($namespace, $key), $default);
}
}
}
function getArgs($namespace)
{
// Get the arguments that are defined for this namespace URI
$namespace = $this->_fixNS($namespace);
if (Auth_OpenID::isFailure($namespace)) {
return $namespace;
} else {
$stuff = array();
foreach ($this->args->items() as $pair) {
list($key, $value) = $pair;
list($pair_ns, $ns_key) = $key;
if ($pair_ns == $namespace) {
$stuff[$ns_key] = $value;
}
}
return $stuff;
}
}
function updateArgs($namespace, $updates)
{
// Set multiple key/value pairs in one call
$namespace = $this->_fixNS($namespace);
if (Auth_OpenID::isFailure($namespace)) {
return $namespace;
} else {
foreach ($updates as $k => $v) {
$this->setArg($namespace, $k, $v);
}
return true;
}
}
function setArg($namespace, $key, $value)
{
// Set a single argument in this namespace
$namespace = $this->_fixNS($namespace);
if (Auth_OpenID::isFailure($namespace)) {
return $namespace;
} else {
$this->args->set(array($namespace, $key), $value);
if ($namespace !== Auth_OpenID_BARE_NS) {
$this->namespaces->add($namespace);
}
return true;
}
}
function delArg($namespace, $key)
{
$namespace = $this->_fixNS($namespace);
if (Auth_OpenID::isFailure($namespace)) {
return $namespace;
} else {
return $this->args->del(array($namespace, $key));
}
}
function getAliasedArg($aliased_key, $default = null)
{
if ($aliased_key == 'ns') {
// Return the namespace URI for the OpenID namespace
return $this->getOpenIDNamespace();
}
$parts = explode('.', $aliased_key, 2);
if (count($parts) != 2) {
$ns = null;
} else {
list($alias, $key) = $parts;
if ($alias == 'ns') {
// Return the namespace URI for a namespace alias
// parameter.
return $this->namespaces->getNamespaceURI($key);
} else {
$ns = $this->namespaces->getNamespaceURI($alias);
}
}
if ($ns === null) {
$key = $aliased_key;
$ns = $this->getOpenIDNamespace();
}
return $this->getArg($ns, $key, $default);
}
}

View file

@ -1,77 +0,0 @@
<?php
/**
* A MySQL store.
*
* @package OpenID
*/
/**
* Require the base class file.
*/
require_once "Auth/OpenID/SQLStore.php";
/**
* An SQL store that uses MySQL as its backend.
*
* @package OpenID
*/
class Auth_OpenID_MySQLStore extends Auth_OpenID_SQLStore {
/**
* @access private
*/
function setSQL()
{
$this->sql['nonce_table'] =
"CREATE TABLE %s (\n".
" server_url VARCHAR(2047) NOT NULL,\n".
" timestamp INTEGER NOT NULL,\n".
" salt CHAR(40) NOT NULL,\n".
" UNIQUE (server_url(255), timestamp, salt)\n".
") ENGINE=InnoDB";
$this->sql['assoc_table'] =
"CREATE TABLE %s (\n".
" server_url VARCHAR(2047) NOT NULL,\n".
" handle VARCHAR(255) NOT NULL,\n".
" secret BLOB NOT NULL,\n".
" issued INTEGER NOT NULL,\n".
" lifetime INTEGER NOT NULL,\n".
" assoc_type VARCHAR(64) NOT NULL,\n".
" PRIMARY KEY (server_url(255), handle)\n".
") ENGINE=InnoDB";
$this->sql['set_assoc'] =
"REPLACE INTO %s (server_url, handle, secret, issued,\n".
" lifetime, assoc_type) VALUES (?, ?, !, ?, ?, ?)";
$this->sql['get_assocs'] =
"SELECT handle, secret, issued, lifetime, assoc_type FROM %s ".
"WHERE server_url = ?";
$this->sql['get_assoc'] =
"SELECT handle, secret, issued, lifetime, assoc_type FROM %s ".
"WHERE server_url = ? AND handle = ?";
$this->sql['remove_assoc'] =
"DELETE FROM %s WHERE server_url = ? AND handle = ?";
$this->sql['add_nonce'] =
"INSERT INTO %s (server_url, timestamp, salt) VALUES (?, ?, ?)";
$this->sql['clean_nonce'] =
"DELETE FROM %s WHERE timestamp < ?";
$this->sql['clean_assoc'] =
"DELETE FROM %s WHERE issued + lifetime < ?";
}
/**
* @access private
*/
function blobEncode($blob)
{
return "0x" . bin2hex($blob);
}
}

View file

@ -1,108 +0,0 @@
<?php
/**
* Nonce-related functionality.
*
* @package OpenID
*/
/**
* Need CryptUtil to generate random strings.
*/
require_once 'Auth/OpenID/CryptUtil.php';
/**
* This is the characters that the nonces are made from.
*/
define('Auth_OpenID_Nonce_CHRS',"abcdefghijklmnopqrstuvwxyz" .
"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
// Keep nonces for five hours (allow five hours for the combination of
// request time and clock skew). This is probably way more than is
// necessary, but there is not much overhead in storing nonces.
global $Auth_OpenID_SKEW;
$Auth_OpenID_SKEW = 60 * 60 * 5;
define('Auth_OpenID_Nonce_REGEX',
'/(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)Z(.*)/');
define('Auth_OpenID_Nonce_TIME_FMT',
'%Y-%m-%dT%H:%M:%SZ');
function Auth_OpenID_splitNonce($nonce_string)
{
// Extract a timestamp from the given nonce string
$result = preg_match(Auth_OpenID_Nonce_REGEX, $nonce_string, $matches);
if ($result != 1 || count($matches) != 8) {
return null;
}
list($unused,
$tm_year,
$tm_mon,
$tm_mday,
$tm_hour,
$tm_min,
$tm_sec,
$uniquifier) = $matches;
$timestamp =
@gmmktime($tm_hour, $tm_min, $tm_sec, $tm_mon, $tm_mday, $tm_year);
if ($timestamp === false || $timestamp < 0) {
return null;
}
return array($timestamp, $uniquifier);
}
function Auth_OpenID_checkTimestamp($nonce_string,
$allowed_skew = null,
$now = null)
{
// Is the timestamp that is part of the specified nonce string
// within the allowed clock-skew of the current time?
global $Auth_OpenID_SKEW;
if ($allowed_skew === null) {
$allowed_skew = $Auth_OpenID_SKEW;
}
$parts = Auth_OpenID_splitNonce($nonce_string);
if ($parts == null) {
return false;
}
if ($now === null) {
$now = time();
}
$stamp = $parts[0];
// Time after which we should not use the nonce
$past = $now - $allowed_skew;
// Time that is too far in the future for us to allow
$future = $now + $allowed_skew;
// the stamp is not too far in the future and is not too far
// in the past
return (($past <= $stamp) && ($stamp <= $future));
}
function Auth_OpenID_mkNonce($when = null)
{
// Generate a nonce with the current timestamp
$salt = Auth_OpenID_CryptUtil::randomString(
6, Auth_OpenID_Nonce_CHRS);
if ($when === null) {
// It's safe to call time() with no arguments; it returns a
// GMT unix timestamp on PHP 4 and PHP 5. gmmktime() with no
// args returns a local unix timestamp on PHP 4, so don't use
// that.
$when = time();
}
$time_str = gmstrftime(Auth_OpenID_Nonce_TIME_FMT, $when);
return $time_str . $salt;
}

View file

@ -1,300 +0,0 @@
<?php
/**
* An implementation of the OpenID Provider Authentication Policy
* Extension 1.0
*
* See:
* http://openid.net/developers/specs/
*/
require_once "Auth/OpenID/Extension.php";
define('Auth_OpenID_PAPE_NS_URI',
"http://specs.openid.net/extensions/pape/1.0");
define('PAPE_AUTH_MULTI_FACTOR_PHYSICAL',
'http://schemas.openid.net/pape/policies/2007/06/multi-factor-physical');
define('PAPE_AUTH_MULTI_FACTOR',
'http://schemas.openid.net/pape/policies/2007/06/multi-factor');
define('PAPE_AUTH_PHISHING_RESISTANT',
'http://schemas.openid.net/pape/policies/2007/06/phishing-resistant');
define('PAPE_TIME_VALIDATOR',
'/^[0-9]{4,4}-[0-9][0-9]-[0-9][0-9]T[0-9][0-9]:[0-9][0-9]:[0-9][0-9]Z$/');
/**
* A Provider Authentication Policy request, sent from a relying party
* to a provider
*
* preferred_auth_policies: The authentication policies that
* the relying party prefers
*
* max_auth_age: The maximum time, in seconds, that the relying party
* wants to allow to have elapsed before the user must re-authenticate
*/
class Auth_OpenID_PAPE_Request extends Auth_OpenID_Extension {
var $ns_alias = 'pape';
var $ns_uri = Auth_OpenID_PAPE_NS_URI;
function Auth_OpenID_PAPE_Request($preferred_auth_policies=null,
$max_auth_age=null)
{
if ($preferred_auth_policies === null) {
$preferred_auth_policies = array();
}
$this->preferred_auth_policies = $preferred_auth_policies;
$this->max_auth_age = $max_auth_age;
}
/**
* Add an acceptable authentication policy URI to this request
*
* This method is intended to be used by the relying party to add
* acceptable authentication types to the request.
*
* policy_uri: The identifier for the preferred type of
* authentication.
*/
function addPolicyURI($policy_uri)
{
if (!in_array($policy_uri, $this->preferred_auth_policies)) {
$this->preferred_auth_policies[] = $policy_uri;
}
}
function getExtensionArgs()
{
$ns_args = array(
'preferred_auth_policies' =>
implode(' ', $this->preferred_auth_policies)
);
if ($this->max_auth_age !== null) {
$ns_args['max_auth_age'] = strval($this->max_auth_age);
}
return $ns_args;
}
/**
* Instantiate a Request object from the arguments in a checkid_*
* OpenID message
*/
static function fromOpenIDRequest($request)
{
$obj = new Auth_OpenID_PAPE_Request();
$args = $request->message->getArgs(Auth_OpenID_PAPE_NS_URI);
if ($args === null || $args === array()) {
return null;
}
$obj->parseExtensionArgs($args);
return $obj;
}
/**
* Set the state of this request to be that expressed in these
* PAPE arguments
*
* @param args: The PAPE arguments without a namespace
*/
function parseExtensionArgs($args)
{
// preferred_auth_policies is a space-separated list of policy
// URIs
$this->preferred_auth_policies = array();
$policies_str = Auth_OpenID::arrayGet($args, 'preferred_auth_policies');
if ($policies_str) {
foreach (explode(' ', $policies_str) as $uri) {
if (!in_array($uri, $this->preferred_auth_policies)) {
$this->preferred_auth_policies[] = $uri;
}
}
}
// max_auth_age is base-10 integer number of seconds
$max_auth_age_str = Auth_OpenID::arrayGet($args, 'max_auth_age');
if ($max_auth_age_str) {
$this->max_auth_age = Auth_OpenID::intval($max_auth_age_str);
} else {
$this->max_auth_age = null;
}
}
/**
* Given a list of authentication policy URIs that a provider
* supports, this method returns the subsequence of those types
* that are preferred by the relying party.
*
* @param supported_types: A sequence of authentication policy
* type URIs that are supported by a provider
*
* @return array The sub-sequence of the supported types that are
* preferred by the relying party. This list will be ordered in
* the order that the types appear in the supported_types
* sequence, and may be empty if the provider does not prefer any
* of the supported authentication types.
*/
function preferredTypes($supported_types)
{
$result = array();
foreach ($supported_types as $st) {
if (in_array($st, $this->preferred_auth_policies)) {
$result[] = $st;
}
}
return $result;
}
}
/**
* A Provider Authentication Policy response, sent from a provider to
* a relying party
*/
class Auth_OpenID_PAPE_Response extends Auth_OpenID_Extension {
var $ns_alias = 'pape';
var $ns_uri = Auth_OpenID_PAPE_NS_URI;
function Auth_OpenID_PAPE_Response($auth_policies=null, $auth_time=null,
$nist_auth_level=null)
{
if ($auth_policies) {
$this->auth_policies = $auth_policies;
} else {
$this->auth_policies = array();
}
$this->auth_time = $auth_time;
$this->nist_auth_level = $nist_auth_level;
}
/**
* Add a authentication policy to this response
*
* This method is intended to be used by the provider to add a
* policy that the provider conformed to when authenticating the
* user.
*
* @param policy_uri: The identifier for the preferred type of
* authentication.
*/
function addPolicyURI($policy_uri)
{
if (!in_array($policy_uri, $this->auth_policies)) {
$this->auth_policies[] = $policy_uri;
}
}
/**
* Create an Auth_OpenID_PAPE_Response object from a successful
* OpenID library response.
*
* @param success_response $success_response A SuccessResponse
* from Auth_OpenID_Consumer::complete()
*
* @returns: A provider authentication policy response from the
* data that was supplied with the id_res response.
*/
static function fromSuccessResponse($success_response)
{
$obj = new Auth_OpenID_PAPE_Response();
// PAPE requires that the args be signed.
$args = $success_response->getSignedNS(Auth_OpenID_PAPE_NS_URI);
if ($args === null || $args === array()) {
return null;
}
$result = $obj->parseExtensionArgs($args);
if ($result === false) {
return null;
} else {
return $obj;
}
}
/**
* Parse the provider authentication policy arguments into the
* internal state of this object
*
* @param args: unqualified provider authentication policy
* arguments
*
* @param strict: Whether to return false when bad data is
* encountered
*
* @return null The data is parsed into the internal fields of
* this object.
*/
function parseExtensionArgs($args, $strict=false)
{
$policies_str = Auth_OpenID::arrayGet($args, 'auth_policies');
if ($policies_str && $policies_str != "none") {
$this->auth_policies = explode(" ", $policies_str);
}
$nist_level_str = Auth_OpenID::arrayGet($args, 'nist_auth_level');
if ($nist_level_str !== null) {
$nist_level = Auth_OpenID::intval($nist_level_str);
if ($nist_level === false) {
if ($strict) {
return false;
} else {
$nist_level = null;
}
}
if (0 <= $nist_level && $nist_level < 5) {
$this->nist_auth_level = $nist_level;
} else if ($strict) {
return false;
}
}
$auth_time = Auth_OpenID::arrayGet($args, 'auth_time');
if ($auth_time !== null) {
if (preg_match(PAPE_TIME_VALIDATOR, $auth_time)) {
$this->auth_time = $auth_time;
} else if ($strict) {
return false;
}
}
}
function getExtensionArgs()
{
$ns_args = array();
if (count($this->auth_policies) > 0) {
$ns_args['auth_policies'] = implode(' ', $this->auth_policies);
} else {
$ns_args['auth_policies'] = 'none';
}
if ($this->nist_auth_level !== null) {
if (!in_array($this->nist_auth_level, range(0, 4), true)) {
return false;
}
$ns_args['nist_auth_level'] = strval($this->nist_auth_level);
}
if ($this->auth_time !== null) {
if (!preg_match(PAPE_TIME_VALIDATOR, $this->auth_time)) {
return false;
}
$ns_args['auth_time'] = $this->auth_time;
}
return $ns_args;
}
}

View file

@ -1,381 +0,0 @@
<?php
/**
* This module implements a VERY limited parser that finds <link> tags
* in the head of HTML or XHTML documents and parses out their
* attributes according to the OpenID spec. It is a liberal parser,
* but it requires these things from the data in order to work:
*
* - There must be an open <html> tag
*
* - There must be an open <head> tag inside of the <html> tag
*
* - Only <link>s that are found inside of the <head> tag are parsed
* (this is by design)
*
* - The parser follows the OpenID specification in resolving the
* attributes of the link tags. This means that the attributes DO
* NOT get resolved as they would by an XML or HTML parser. In
* particular, only certain entities get replaced, and href
* attributes do not get resolved relative to a base URL.
*
* From http://openid.net/specs.bml:
*
* - The openid.server URL MUST be an absolute URL. OpenID consumers
* MUST NOT attempt to resolve relative URLs.
*
* - The openid.server URL MUST NOT include entities other than &amp;,
* &lt;, &gt;, and &quot;.
*
* The parser ignores SGML comments and <![CDATA[blocks]]>. Both kinds
* of quoting are allowed for attributes.
*
* The parser deals with invalid markup in these ways:
*
* - Tag names are not case-sensitive
*
* - The <html> tag is accepted even when it is not at the top level
*
* - The <head> tag is accepted even when it is not a direct child of
* the <html> tag, but a <html> tag must be an ancestor of the
* <head> tag
*
* - <link> tags are accepted even when they are not direct children
* of the <head> tag, but a <head> tag must be an ancestor of the
* <link> tag
*
* - If there is no closing tag for an open <html> or <head> tag, the
* remainder of the document is viewed as being inside of the
* tag. If there is no closing tag for a <link> tag, the link tag is
* treated as a short tag. Exceptions to this rule are that <html>
* closes <html> and <body> or <head> closes <head>
*
* - Attributes of the <link> tag are not required to be quoted.
*
* - In the case of duplicated attribute names, the attribute coming
* last in the tag will be the value returned.
*
* - Any text that does not parse as an attribute within a link tag
* will be ignored. (e.g. <link pumpkin rel='openid.server' /> will
* ignore pumpkin)
*
* - If there are more than one <html> or <head> tag, the parser only
* looks inside of the first one.
*
* - The contents of <script> tags are ignored entirely, except
* unclosed <script> tags. Unclosed <script> tags are ignored.
*
* - Any other invalid markup is ignored, including unclosed SGML
* comments and unclosed <![CDATA[blocks.
*
* PHP versions 4 and 5
*
* LICENSE: See the COPYING file included in this distribution.
*
* @access private
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005-2008 Janrain, Inc.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
*/
/**
* Require Auth_OpenID::arrayGet().
*/
require_once "Auth/OpenID.php";
class Auth_OpenID_Parse {
/**
* Specify some flags for use with regex matching.
*/
var $_re_flags = "si";
/**
* Stuff to remove before we start looking for tags
*/
var $_removed_re =
"<!--.*?-->|<!\[CDATA\[.*?\]\]>|<script\b(?!:)[^>]*>.*?<\/script>";
/**
* Starts with the tag name at a word boundary, where the tag name
* is not a namespace
*/
var $_tag_expr = "<%s\b(?!:)([^>]*?)(?:\/>|>(.*)(?:<\/?%s\s*>|\Z))";
var $_attr_find = '\b(\w+)=("[^"]*"|\'[^\']*\'|[^\'"\s\/<>]+)';
var $_open_tag_expr = "<%s\b";
var $_close_tag_expr = "<((\/%s\b)|(%s[^>\/]*\/))>";
function Auth_OpenID_Parse()
{
$this->_link_find = sprintf("/<link\b(?!:)([^>]*)(?!<)>/%s",
$this->_re_flags);
$this->_entity_replacements = array(
'amp' => '&',
'lt' => '<',
'gt' => '>',
'quot' => '"'
);
$this->_attr_find = sprintf("/%s/%s",
$this->_attr_find,
$this->_re_flags);
$this->_removed_re = sprintf("/%s/%s",
$this->_removed_re,
$this->_re_flags);
$this->_ent_replace =
sprintf("&(%s);", implode("|",
$this->_entity_replacements));
}
/**
* Returns a regular expression that will match a given tag in an
* SGML string.
*/
function tagMatcher($tag_name, $close_tags = null)
{
$expr = $this->_tag_expr;
if ($close_tags) {
$options = implode("|", array_merge(array($tag_name), $close_tags));
$closer = sprintf("(?:%s)", $options);
} else {
$closer = $tag_name;
}
$expr = sprintf($expr, $tag_name, $closer);
return sprintf("/%s/%s", $expr, $this->_re_flags);
}
function openTag($tag_name)
{
$expr = sprintf($this->_open_tag_expr, $tag_name);
return sprintf("/%s/%s", $expr, $this->_re_flags);
}
function closeTag($tag_name)
{
$expr = sprintf($this->_close_tag_expr, $tag_name, $tag_name);
return sprintf("/%s/%s", $expr, $this->_re_flags);
}
function htmlBegin($s)
{
$matches = array();
$result = preg_match($this->openTag('html'), $s,
$matches, PREG_OFFSET_CAPTURE);
if ($result === false || !$matches) {
return false;
}
// Return the offset of the first match.
return $matches[0][1];
}
function htmlEnd($s)
{
$matches = array();
$result = preg_match($this->closeTag('html'), $s,
$matches, PREG_OFFSET_CAPTURE);
if ($result === false || !$matches) {
return false;
}
// Return the offset of the first match.
return $matches[count($matches) - 1][1];
}
function headFind()
{
return $this->tagMatcher('head', array('body', 'html'));
}
function replaceEntities($str)
{
foreach ($this->_entity_replacements as $old => $new) {
$str = preg_replace(sprintf("/&%s;/", $old), $new, $str);
}
return $str;
}
function removeQuotes($str)
{
$matches = array();
$double = '/^"(.*)"$/';
$single = "/^\'(.*)\'$/";
if (preg_match($double, $str, $matches)) {
return $matches[1];
} else if (preg_match($single, $str, $matches)) {
return $matches[1];
} else {
return $str;
}
}
function match($regexp, $text, &$match)
{
if (!is_callable('mb_ereg_search_init')) {
if (!preg_match($regexp, $text, $match)) {
return false;
}
$match = $match[0];
return true;
}
$regexp = substr($regexp, 1, strlen($regexp) - 2 - strlen($this->_re_flags));
mb_ereg_search_init($text);
if (!mb_ereg_search($regexp)) {
return false;
}
$match = mb_ereg_search_getregs();
return true;
}
/**
* Find all link tags in a string representing a HTML document and
* return a list of their attributes.
*
* @todo This is quite ineffective and may fail with the default
* pcre.backtrack_limit of 100000 in PHP 5.2, if $html is big.
* It should rather use stripos (in PHP5) or strpos()+strtoupper()
* in PHP4 to manage this.
*
* @param string $html The text to parse
* @return array $list An array of arrays of attributes, one for each
* link tag
*/
function parseLinkAttrs($html)
{
$stripped = preg_replace($this->_removed_re,
"",
$html);
$html_begin = $this->htmlBegin($stripped);
$html_end = $this->htmlEnd($stripped);
if ($html_begin === false) {
return array();
}
if ($html_end === false) {
$html_end = strlen($stripped);
}
$stripped = substr($stripped, $html_begin,
$html_end - $html_begin);
// Workaround to prevent PREG_BACKTRACK_LIMIT_ERROR:
$old_btlimit = ini_set( 'pcre.backtrack_limit', -1 );
// Try to find the <HEAD> tag.
$head_re = $this->headFind();
$head_match = array();
if (!$this->match($head_re, $stripped, $head_match)) {
ini_set( 'pcre.backtrack_limit', $old_btlimit );
return array();
}
$link_data = array();
$link_matches = array();
if (!preg_match_all($this->_link_find, $head_match[0],
$link_matches)) {
ini_set( 'pcre.backtrack_limit', $old_btlimit );
return array();
}
foreach ($link_matches[0] as $link) {
$attr_matches = array();
preg_match_all($this->_attr_find, $link, $attr_matches);
$link_attrs = array();
foreach ($attr_matches[0] as $index => $full_match) {
$name = $attr_matches[1][$index];
$value = $this->replaceEntities(
$this->removeQuotes($attr_matches[2][$index]));
$link_attrs[strtolower($name)] = $value;
}
$link_data[] = $link_attrs;
}
ini_set( 'pcre.backtrack_limit', $old_btlimit );
return $link_data;
}
function relMatches($rel_attr, $target_rel)
{
// Does this target_rel appear in the rel_str?
// XXX: TESTME
$rels = preg_split("/\s+/", trim($rel_attr));
foreach ($rels as $rel) {
$rel = strtolower($rel);
if ($rel == $target_rel) {
return 1;
}
}
return 0;
}
function linkHasRel($link_attrs, $target_rel)
{
// Does this link have target_rel as a relationship?
// XXX: TESTME
$rel_attr = Auth_OpeniD::arrayGet($link_attrs, 'rel', null);
return ($rel_attr && $this->relMatches($rel_attr,
$target_rel));
}
function findLinksRel($link_attrs_list, $target_rel)
{
// Filter the list of link attributes on whether it has
// target_rel as a relationship.
// XXX: TESTME
$result = array();
foreach ($link_attrs_list as $attr) {
if ($this->linkHasRel($attr, $target_rel)) {
$result[] = $attr;
}
}
return $result;
}
function findFirstHref($link_attrs_list, $target_rel)
{
// Return the value of the href attribute for the first link
// tag in the list that has target_rel as a relationship.
// XXX: TESTME
$matches = $this->findLinksRel($link_attrs_list,
$target_rel);
if (!$matches) {
return null;
}
$first = $matches[0];
return Auth_OpenID::arrayGet($first, 'href', null);
}
}
function Auth_OpenID_legacy_discover($html_text, $server_rel,
$delegate_rel)
{
$p = new Auth_OpenID_Parse();
$link_attrs = $p->parseLinkAttrs($html_text);
$server_url = $p->findFirstHref($link_attrs,
$server_rel);
if ($server_url === null) {
return false;
} else {
$delegate_url = $p->findFirstHref($link_attrs,
$delegate_rel);
return array($delegate_url, $server_url);
}
}

View file

@ -1,112 +0,0 @@
<?php
/**
* A PostgreSQL store.
*
* @package OpenID
*/
/**
* Require the base class file.
*/
require_once "Auth/OpenID/SQLStore.php";
/**
* An SQL store that uses PostgreSQL as its backend.
*
* @package OpenID
*/
class Auth_OpenID_PostgreSQLStore extends Auth_OpenID_SQLStore {
/**
* @access private
*/
function setSQL()
{
$this->sql['nonce_table'] =
"CREATE TABLE %s (server_url VARCHAR(2047) NOT NULL, ".
"timestamp INTEGER NOT NULL, ".
"salt CHAR(40) NOT NULL, ".
"UNIQUE (server_url, timestamp, salt))";
$this->sql['assoc_table'] =
"CREATE TABLE %s (server_url VARCHAR(2047) NOT NULL, ".
"handle VARCHAR(255) NOT NULL, ".
"secret BYTEA NOT NULL, ".
"issued INTEGER NOT NULL, ".
"lifetime INTEGER NOT NULL, ".
"assoc_type VARCHAR(64) NOT NULL, ".
"PRIMARY KEY (server_url, handle), ".
"CONSTRAINT secret_length_constraint CHECK ".
"(LENGTH(secret) <= 128))";
$this->sql['set_assoc'] =
array(
'insert_assoc' => "INSERT INTO %s (server_url, handle, ".
"secret, issued, lifetime, assoc_type) VALUES ".
"(?, ?, '!', ?, ?, ?)",
'update_assoc' => "UPDATE %s SET secret = '!', issued = ?, ".
"lifetime = ?, assoc_type = ? WHERE server_url = ? AND ".
"handle = ?"
);
$this->sql['get_assocs'] =
"SELECT handle, secret, issued, lifetime, assoc_type FROM %s ".
"WHERE server_url = ?";
$this->sql['get_assoc'] =
"SELECT handle, secret, issued, lifetime, assoc_type FROM %s ".
"WHERE server_url = ? AND handle = ?";
$this->sql['remove_assoc'] =
"DELETE FROM %s WHERE server_url = ? AND handle = ?";
$this->sql['add_nonce'] =
"INSERT INTO %s (server_url, timestamp, salt) VALUES ".
"(?, ?, ?)"
;
$this->sql['clean_nonce'] =
"DELETE FROM %s WHERE timestamp < ?";
$this->sql['clean_assoc'] =
"DELETE FROM %s WHERE issued + lifetime < ?";
}
/**
* @access private
*/
function _set_assoc($server_url, $handle, $secret, $issued, $lifetime,
$assoc_type)
{
$result = $this->_get_assoc($server_url, $handle);
if ($result) {
// Update the table since this associations already exists.
$this->connection->query($this->sql['set_assoc']['update_assoc'],
array($secret, $issued, $lifetime,
$assoc_type, $server_url, $handle));
} else {
// Insert a new record because this association wasn't
// found.
$this->connection->query($this->sql['set_assoc']['insert_assoc'],
array($server_url, $handle, $secret,
$issued, $lifetime, $assoc_type));
}
}
/**
* @access private
*/
function blobEncode($blob)
{
return $this->_octify($blob);
}
/**
* @access private
*/
function blobDecode($blob)
{
return $this->_unoctify($blob);
}
}

View file

@ -1,208 +0,0 @@
<?php
/**
* Supplies Redis server store backend for OpenID servers and consumers.
* Uses Predis library {@see https://github.com/nrk/predis}.
* Requires PHP >= 5.3.
*
* LICENSE: See the COPYING file included in this distribution.
*
* @package OpenID
* @author Ville Mattila <ville@eventio.fi>
* @copyright 2008 JanRain Inc., 2013 Eventio Oy / Ville Mattila
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
* Contributed by Eventio Oy <http://www.eventio.fi/>
*/
/**
* Import the interface for creating a new store class.
*/
require_once 'Auth/OpenID/Interface.php';
/**
* Supplies Redis server store backend for OpenID servers and consumers.
* Uses Predis library {@see https://github.com/nrk/predis}.
* Requires PHP >= 5.3.
*
* @package OpenID
*/
class Auth_OpenID_PredisStore extends Auth_OpenID_OpenIDStore {
/**
* @var \Predis\Client
*/
protected $redis;
/**
* Prefix for Redis keys
* @var string
*/
protected $prefix;
/**
* Initializes a new {@link Auth_OpenID_PredisStore} instance.
*
* @param \Predis\Client $redis Predis client object
* @param string $prefix Prefix for all keys stored to the Redis
*/
function Auth_OpenID_PredisStore(\Predis\Client $redis, $prefix = '')
{
$this->prefix = $prefix;
$this->redis = $redis;
}
/**
* Store association until its expiration time in Redis server.
* Overwrites any existing association with same server_url and
* handle. Handles list of associations for every server.
*/
function storeAssociation($server_url, $association)
{
// create Redis keys for association itself
// and list of associations for this server
$associationKey = $this->associationKey($server_url,
$association->handle);
$serverKey = $this->associationServerKey($server_url);
// save association to server's associations' keys list
$this->redis->lpush(
$serverKey,
$associationKey
);
// Will touch the association list expiration, to avoid filling up
$newExpiration = ($association->issued + $association->lifetime);
$expirationKey = $serverKey.'_expires_at';
$expiration = $this->redis->get($expirationKey);
if (!$expiration || $newExpiration > $expiration) {
$this->redis->set($expirationKey, $newExpiration);
$this->redis->expireat($serverKey, $newExpiration);
$this->redis->expireat($expirationKey, $newExpiration);
}
// save association itself, will automatically expire
$this->redis->setex(
$associationKey,
$newExpiration - time(),
serialize($association)
);
}
/**
* Read association from Redis. If no handle given
* and multiple associations found, returns latest issued
*/
function getAssociation($server_url, $handle = null)
{
// simple case: handle given
if ($handle !== null) {
return $this->getAssociationFromServer(
$this->associationKey($server_url, $handle)
);
}
// no handle given, receiving the latest issued
$serverKey = $this->associationServerKey($server_url);
$lastKey = $this->redis->lpop($serverKey);
if (!$lastKey) { return null; }
// get association, return null if failed
return $this->getAssociationFromServer($lastKey);
}
/**
* Function to actually receive and unserialize the association
* from the server.
*/
private function getAssociationFromServer($associationKey)
{
$association = $this->redis->get($associationKey);
return $association ? unserialize($association) : null;
}
/**
* Immediately delete association from Redis.
*/
function removeAssociation($server_url, $handle)
{
// create Redis keys
$serverKey = $this->associationServerKey($server_url);
$associationKey = $this->associationKey($server_url,
$handle);
// Removing the association from the server's association list
$removed = $this->redis->lrem($serverKey, 0, $associationKey);
if ($removed < 1) {
return false;
}
// Delete the association itself
return $this->redis->del($associationKey);
}
/**
* Create nonce for server and salt, expiring after
* $Auth_OpenID_SKEW seconds.
*/
function useNonce($server_url, $timestamp, $salt)
{
global $Auth_OpenID_SKEW;
// save one request to memcache when nonce obviously expired
if (abs($timestamp - time()) > $Auth_OpenID_SKEW) {
return false;
}
// SETNX will set the value only of the key doesn't exist yet.
$nonceKey = $this->nonceKey($server_url, $salt);
$added = $this->predis->setnx($nonceKey);
if ($added) {
// Will set expiration
$this->predis->expire($nonceKey, $Auth_OpenID_SKEW);
return true;
} else {
return false;
}
}
/**
* Build up nonce key
*/
private function nonceKey($server_url, $salt)
{
return $this->prefix .
'openid_nonce_' .
sha1($server_url) . '_' . sha1($salt);
}
/**
* Key is prefixed with $prefix and 'openid_association_' string
*/
function associationKey($server_url, $handle = null)
{
return $this->prefix .
'openid_association_' .
sha1($server_url) . '_' . sha1($handle);
}
/**
* Key is prefixed with $prefix and 'openid_association_server_' string
*/
function associationServerKey($server_url)
{
return $this->prefix .
'openid_association_server_' .
sha1($server_url);
}
/**
* Report that this storage doesn't support cleanup
*/
function supportsCleanup()
{
return false;
}
}

View file

@ -1,557 +0,0 @@
<?php
/**
* SQL-backed OpenID stores.
*
* PHP versions 4 and 5
*
* LICENSE: See the COPYING file included in this distribution.
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005-2008 Janrain, Inc.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
*/
/**
* @access private
*/
require_once 'Auth/OpenID/Interface.php';
require_once 'Auth/OpenID/Nonce.php';
/**
* @access private
*/
require_once 'Auth/OpenID.php';
/**
* @access private
*/
require_once 'Auth/OpenID/Nonce.php';
/**
* This is the parent class for the SQL stores, which contains the
* logic common to all of the SQL stores.
*
* The table names used are determined by the class variables
* associations_table_name and nonces_table_name. To change the name
* of the tables used, pass new table names into the constructor.
*
* To create the tables with the proper schema, see the createTables
* method.
*
* This class shouldn't be used directly. Use one of its subclasses
* instead, as those contain the code necessary to use a specific
* database. If you're an OpenID integrator and you'd like to create
* an SQL-driven store that wraps an application's database
* abstraction, be sure to create a subclass of
* {@link Auth_OpenID_DatabaseConnection} that calls the application's
* database abstraction calls. Then, pass an instance of your new
* database connection class to your SQLStore subclass constructor.
*
* All methods other than the constructor and createTables should be
* considered implementation details.
*
* @package OpenID
*/
class Auth_OpenID_SQLStore extends Auth_OpenID_OpenIDStore {
/**
* This creates a new SQLStore instance. It requires an
* established database connection be given to it, and it allows
* overriding the default table names.
*
* @param connection $connection This must be an established
* connection to a database of the correct type for the SQLStore
* subclass you're using. This must either be an PEAR DB
* connection handle or an instance of a subclass of
* Auth_OpenID_DatabaseConnection.
*
* @param associations_table: This is an optional parameter to
* specify the name of the table used for storing associations.
* The default value is 'oid_associations'.
*
* @param nonces_table: This is an optional parameter to specify
* the name of the table used for storing nonces. The default
* value is 'oid_nonces'.
*/
function Auth_OpenID_SQLStore($connection,
$associations_table = null,
$nonces_table = null)
{
$this->associations_table_name = "oid_associations";
$this->nonces_table_name = "oid_nonces";
// Check the connection object type to be sure it's a PEAR
// database connection.
if (!(is_object($connection) &&
(is_subclass_of($connection, 'db_common') ||
is_subclass_of($connection,
'auth_openid_databaseconnection')))) {
trigger_error("Auth_OpenID_SQLStore expected PEAR connection " .
"object (got ".get_class($connection).")",
E_USER_ERROR);
return;
}
$this->connection = $connection;
// Be sure to set the fetch mode so the results are keyed on
// column name instead of column index. This is a PEAR
// constant, so only try to use it if PEAR is present. Note
// that Auth_Openid_Databaseconnection instances need not
// implement ::setFetchMode for this reason.
if (is_subclass_of($this->connection, 'db_common')) {
$this->connection->setFetchMode(DB_FETCHMODE_ASSOC);
}
if ($associations_table) {
$this->associations_table_name = $associations_table;
}
if ($nonces_table) {
$this->nonces_table_name = $nonces_table;
}
$this->max_nonce_age = 6 * 60 * 60;
// Be sure to run the database queries with auto-commit mode
// turned OFF, because we want every function to run in a
// transaction, implicitly. As a rule, methods named with a
// leading underscore will NOT control transaction behavior.
// Callers of these methods will worry about transactions.
$this->connection->autoCommit(false);
// Create an empty SQL strings array.
$this->sql = array();
// Call this method (which should be overridden by subclasses)
// to populate the $this->sql array with SQL strings.
$this->setSQL();
// Verify that all required SQL statements have been set, and
// raise an error if any expected SQL strings were either
// absent or empty.
list($missing, $empty) = $this->_verifySQL();
if ($missing) {
trigger_error("Expected keys in SQL query list: " .
implode(", ", $missing),
E_USER_ERROR);
return;
}
if ($empty) {
trigger_error("SQL list keys have no SQL strings: " .
implode(", ", $empty),
E_USER_ERROR);
return;
}
// Add table names to queries.
$this->_fixSQL();
}
function tableExists($table_name)
{
return !$this->isError(
$this->connection->query(
sprintf("SELECT * FROM %s LIMIT 0",
$table_name)));
}
/**
* Returns true if $value constitutes a database error; returns
* false otherwise.
*/
function isError($value)
{
return @PEAR::isError($value);
}
/**
* Converts a query result to a boolean. If the result is a
* database error according to $this->isError(), this returns
* false; otherwise, this returns true.
*/
function resultToBool($obj)
{
if ($this->isError($obj)) {
return false;
} else {
return true;
}
}
/**
* This method should be overridden by subclasses. This method is
* called by the constructor to set values in $this->sql, which is
* an array keyed on sql name.
*/
function setSQL()
{
}
/**
* Resets the store by removing all records from the store's
* tables.
*/
function reset()
{
$this->connection->query(sprintf("DELETE FROM %s",
$this->associations_table_name));
$this->connection->query(sprintf("DELETE FROM %s",
$this->nonces_table_name));
}
/**
* @access private
*/
function _verifySQL()
{
$missing = array();
$empty = array();
$required_sql_keys = array(
'nonce_table',
'assoc_table',
'set_assoc',
'get_assoc',
'get_assocs',
'remove_assoc'
);
foreach ($required_sql_keys as $key) {
if (!array_key_exists($key, $this->sql)) {
$missing[] = $key;
} else if (!$this->sql[$key]) {
$empty[] = $key;
}
}
return array($missing, $empty);
}
/**
* @access private
*/
function _fixSQL()
{
$replacements = array(
array(
'value' => $this->nonces_table_name,
'keys' => array('nonce_table',
'add_nonce',
'clean_nonce')
),
array(
'value' => $this->associations_table_name,
'keys' => array('assoc_table',
'set_assoc',
'get_assoc',
'get_assocs',
'remove_assoc',
'clean_assoc')
)
);
foreach ($replacements as $item) {
$value = $item['value'];
$keys = $item['keys'];
foreach ($keys as $k) {
if (is_array($this->sql[$k])) {
foreach ($this->sql[$k] as $part_key => $part_value) {
$this->sql[$k][$part_key] = sprintf($part_value,
$value);
}
} else {
$this->sql[$k] = sprintf($this->sql[$k], $value);
}
}
}
}
function blobDecode($blob)
{
return $blob;
}
function blobEncode($str)
{
return $str;
}
function createTables()
{
$this->connection->autoCommit(true);
$n = $this->create_nonce_table();
$a = $this->create_assoc_table();
$this->connection->autoCommit(false);
if ($n && $a) {
return true;
} else {
return false;
}
}
function create_nonce_table()
{
if (!$this->tableExists($this->nonces_table_name)) {
$r = $this->connection->query($this->sql['nonce_table']);
return $this->resultToBool($r);
}
return true;
}
function create_assoc_table()
{
if (!$this->tableExists($this->associations_table_name)) {
$r = $this->connection->query($this->sql['assoc_table']);
return $this->resultToBool($r);
}
return true;
}
/**
* @access private
*/
function _set_assoc($server_url, $handle, $secret, $issued,
$lifetime, $assoc_type)
{
return $this->connection->query($this->sql['set_assoc'],
array(
$server_url,
$handle,
$secret,
$issued,
$lifetime,
$assoc_type));
}
function storeAssociation($server_url, $association)
{
if ($this->resultToBool($this->_set_assoc(
$server_url,
$association->handle,
$this->blobEncode(
$association->secret),
$association->issued,
$association->lifetime,
$association->assoc_type
))) {
$this->connection->commit();
} else {
$this->connection->rollback();
}
}
/**
* @access private
*/
function _get_assoc($server_url, $handle)
{
$result = $this->connection->getRow($this->sql['get_assoc'],
array($server_url, $handle));
if ($this->isError($result)) {
return null;
} else {
return $result;
}
}
/**
* @access private
*/
function _get_assocs($server_url)
{
$result = $this->connection->getAll($this->sql['get_assocs'],
array($server_url));
if ($this->isError($result)) {
return array();
} else {
return $result;
}
}
function removeAssociation($server_url, $handle)
{
if ($this->_get_assoc($server_url, $handle) == null) {
return false;
}
if ($this->resultToBool($this->connection->query(
$this->sql['remove_assoc'],
array($server_url, $handle)))) {
$this->connection->commit();
} else {
$this->connection->rollback();
}
return true;
}
function getAssociation($server_url, $handle = null)
{
if ($handle !== null) {
$assoc = $this->_get_assoc($server_url, $handle);
$assocs = array();
if ($assoc) {
$assocs[] = $assoc;
}
} else {
$assocs = $this->_get_assocs($server_url);
}
if (!$assocs || (count($assocs) == 0)) {
return null;
} else {
$associations = array();
foreach ($assocs as $assoc_row) {
$assoc = new Auth_OpenID_Association($assoc_row['handle'],
$assoc_row['secret'],
$assoc_row['issued'],
$assoc_row['lifetime'],
$assoc_row['assoc_type']);
$assoc->secret = $this->blobDecode($assoc->secret);
if ($assoc->getExpiresIn() == 0) {
$this->removeAssociation($server_url, $assoc->handle);
} else {
$associations[] = array($assoc->issued, $assoc);
}
}
if ($associations) {
$issued = array();
$assocs = array();
foreach ($associations as $key => $assoc) {
$issued[$key] = $assoc[0];
$assocs[$key] = $assoc[1];
}
array_multisort($issued, SORT_DESC, $assocs, SORT_DESC,
$associations);
// return the most recently issued one.
list($issued, $assoc) = $associations[0];
return $assoc;
} else {
return null;
}
}
}
/**
* @access private
*/
function _add_nonce($server_url, $timestamp, $salt)
{
$sql = $this->sql['add_nonce'];
$result = $this->connection->query($sql, array($server_url,
$timestamp,
$salt));
if ($this->isError($result)) {
$this->connection->rollback();
} else {
$this->connection->commit();
}
return $this->resultToBool($result);
}
function useNonce($server_url, $timestamp, $salt)
{
global $Auth_OpenID_SKEW;
if ( abs($timestamp - time()) > $Auth_OpenID_SKEW ) {
return false;
}
return $this->_add_nonce($server_url, $timestamp, $salt);
}
/**
* "Octifies" a binary string by returning a string with escaped
* octal bytes. This is used for preparing binary data for
* PostgreSQL BYTEA fields.
*
* @access private
*/
function _octify($str)
{
$result = "";
for ($i = 0; $i < Auth_OpenID::bytes($str); $i++) {
$ch = substr($str, $i, 1);
if ($ch == "\\") {
$result .= "\\\\\\\\";
} else if (ord($ch) == 0) {
$result .= "\\\\000";
} else {
$result .= "\\" . strval(decoct(ord($ch)));
}
}
return $result;
}
/**
* "Unoctifies" octal-escaped data from PostgreSQL and returns the
* resulting ASCII (possibly binary) string.
*
* @access private
*/
function _unoctify($str)
{
$result = "";
$i = 0;
while ($i < strlen($str)) {
$char = $str[$i];
if ($char == "\\") {
// Look to see if the next char is a backslash and
// append it.
if ($str[$i + 1] != "\\") {
$octal_digits = substr($str, $i + 1, 3);
$dec = octdec($octal_digits);
$char = chr($dec);
$i += 4;
} else {
$char = "\\";
$i += 2;
}
} else {
$i += 1;
}
$result .= $char;
}
return $result;
}
function cleanupNonces()
{
global $Auth_OpenID_SKEW;
$v = time() - $Auth_OpenID_SKEW;
$this->connection->query($this->sql['clean_nonce'], array($v));
$num = $this->connection->affectedRows();
$this->connection->commit();
return $num;
}
function cleanupAssociations()
{
$this->connection->query($this->sql['clean_assoc'],
array(time()));
$num = $this->connection->affectedRows();
$this->connection->commit();
return $num;
}
}

View file

@ -1,70 +0,0 @@
<?php
/**
* An SQLite store.
*
* @package OpenID
*/
/**
* Require the base class file.
*/
require_once "Auth/OpenID/SQLStore.php";
/**
* An SQL store that uses SQLite as its backend.
*
* @package OpenID
*/
class Auth_OpenID_SQLiteStore extends Auth_OpenID_SQLStore {
function setSQL()
{
$this->sql['nonce_table'] =
"CREATE TABLE %s (server_url VARCHAR(2047), timestamp INTEGER, ".
"salt CHAR(40), UNIQUE (server_url, timestamp, salt))";
$this->sql['assoc_table'] =
"CREATE TABLE %s (server_url VARCHAR(2047), handle VARCHAR(255), ".
"secret BLOB(128), issued INTEGER, lifetime INTEGER, ".
"assoc_type VARCHAR(64), PRIMARY KEY (server_url, handle))";
$this->sql['set_assoc'] =
"INSERT OR REPLACE INTO %s VALUES (?, ?, ?, ?, ?, ?)";
$this->sql['get_assocs'] =
"SELECT handle, secret, issued, lifetime, assoc_type FROM %s ".
"WHERE server_url = ?";
$this->sql['get_assoc'] =
"SELECT handle, secret, issued, lifetime, assoc_type FROM %s ".
"WHERE server_url = ? AND handle = ?";
$this->sql['remove_assoc'] =
"DELETE FROM %s WHERE server_url = ? AND handle = ?";
$this->sql['add_nonce'] =
"INSERT INTO %s (server_url, timestamp, salt) VALUES (?, ?, ?)";
$this->sql['clean_nonce'] =
"DELETE FROM %s WHERE timestamp < ?";
$this->sql['clean_assoc'] =
"DELETE FROM %s WHERE issued + lifetime < ?";
}
/**
* @access private
*/
function _add_nonce($server_url, $timestamp, $salt)
{
// PECL SQLite extensions 1.0.3 and older (1.0.3 is the
// current release at the time of this writing) have a broken
// sqlite_escape_string function that breaks when passed the
// empty string. Prefixing all strings with one character
// keeps them unique and avoids this bug. The nonce table is
// write-only, so we don't have to worry about updating other
// functions with this same bad hack.
return parent::_add_nonce('x' . $server_url, $timestamp, $salt);
}
}

View file

@ -1,521 +0,0 @@
<?php
/**
* Simple registration request and response parsing and object
* representation.
*
* This module contains objects representing simple registration
* requests and responses that can be used with both OpenID relying
* parties and OpenID providers.
*
* 1. The relying party creates a request object and adds it to the
* {@link Auth_OpenID_AuthRequest} object before making the
* checkid request to the OpenID provider:
*
* $sreg_req = Auth_OpenID_SRegRequest::build(array('email'));
* $auth_request->addExtension($sreg_req);
*
* 2. The OpenID provider extracts the simple registration request
* from the OpenID request using {@link
* Auth_OpenID_SRegRequest::fromOpenIDRequest}, gets the user's
* approval and data, creates an {@link Auth_OpenID_SRegResponse}
* object and adds it to the id_res response:
*
* $sreg_req = Auth_OpenID_SRegRequest::fromOpenIDRequest(
* $checkid_request);
* // [ get the user's approval and data, informing the user that
* // the fields in sreg_response were requested ]
* $sreg_resp = Auth_OpenID_SRegResponse::extractResponse(
* $sreg_req, $user_data);
* $sreg_resp->toMessage($openid_response->fields);
*
* 3. The relying party uses {@link
* Auth_OpenID_SRegResponse::fromSuccessResponse} to extract the data
* from the OpenID response:
*
* $sreg_resp = Auth_OpenID_SRegResponse::fromSuccessResponse(
* $success_response);
*
* @package OpenID
*/
/**
* Import message and extension internals.
*/
require_once 'Auth/OpenID/Message.php';
require_once 'Auth/OpenID/Extension.php';
// The data fields that are listed in the sreg spec
global $Auth_OpenID_sreg_data_fields;
$Auth_OpenID_sreg_data_fields = array(
'fullname' => 'Full Name',
'nickname' => 'Nickname',
'dob' => 'Date of Birth',
'email' => 'E-mail Address',
'gender' => 'Gender',
'postcode' => 'Postal Code',
'country' => 'Country',
'language' => 'Language',
'timezone' => 'Time Zone');
/**
* Check to see that the given value is a valid simple registration
* data field name. Return true if so, false if not.
*/
function Auth_OpenID_checkFieldName($field_name)
{
global $Auth_OpenID_sreg_data_fields;
if (!in_array($field_name, array_keys($Auth_OpenID_sreg_data_fields))) {
return false;
}
return true;
}
// URI used in the wild for Yadis documents advertising simple
// registration support
define('Auth_OpenID_SREG_NS_URI_1_0', 'http://openid.net/sreg/1.0');
// URI in the draft specification for simple registration 1.1
// <http://openid.net/specs/openid-simple-registration-extension-1_1-01.html>
define('Auth_OpenID_SREG_NS_URI_1_1', 'http://openid.net/extensions/sreg/1.1');
// This attribute will always hold the preferred URI to use when
// adding sreg support to an XRDS file or in an OpenID namespace
// declaration.
define('Auth_OpenID_SREG_NS_URI', Auth_OpenID_SREG_NS_URI_1_1);
Auth_OpenID_registerNamespaceAlias(Auth_OpenID_SREG_NS_URI_1_1, 'sreg');
/**
* Does the given endpoint advertise support for simple
* registration?
*
* $endpoint: The endpoint object as returned by OpenID discovery.
* returns whether an sreg type was advertised by the endpoint
*/
function Auth_OpenID_supportsSReg($endpoint)
{
return ($endpoint->usesExtension(Auth_OpenID_SREG_NS_URI_1_1) ||
$endpoint->usesExtension(Auth_OpenID_SREG_NS_URI_1_0));
}
/**
* A base class for classes dealing with Simple Registration protocol
* messages.
*
* @package OpenID
*/
class Auth_OpenID_SRegBase extends Auth_OpenID_Extension {
/**
* Extract the simple registration namespace URI from the given
* OpenID message. Handles OpenID 1 and 2, as well as both sreg
* namespace URIs found in the wild, as well as missing namespace
* definitions (for OpenID 1)
*
* $message: The OpenID message from which to parse simple
* registration fields. This may be a request or response message.
*
* Returns the sreg namespace URI for the supplied message. The
* message may be modified to define a simple registration
* namespace.
*
* @access private
*/
static function _getSRegNS($message)
{
$alias = null;
$found_ns_uri = null;
// See if there exists an alias for one of the two defined
// simple registration types.
foreach (array(Auth_OpenID_SREG_NS_URI_1_1,
Auth_OpenID_SREG_NS_URI_1_0) as $sreg_ns_uri) {
$alias = $message->namespaces->getAlias($sreg_ns_uri);
if ($alias !== null) {
$found_ns_uri = $sreg_ns_uri;
break;
}
}
if ($alias === null) {
// There is no alias for either of the types, so try to
// add one. We default to using the modern value (1.1)
$found_ns_uri = Auth_OpenID_SREG_NS_URI_1_1;
if ($message->namespaces->addAlias(Auth_OpenID_SREG_NS_URI_1_1,
'sreg') === null) {
// An alias for the string 'sreg' already exists, but
// it's defined for something other than simple
// registration
return null;
}
}
return $found_ns_uri;
}
}
/**
* An object to hold the state of a simple registration request.
*
* required: A list of the required fields in this simple registration
* request
*
* optional: A list of the optional fields in this simple registration
* request
*
* @package OpenID
*/
class Auth_OpenID_SRegRequest extends Auth_OpenID_SRegBase {
var $ns_alias = 'sreg';
/**
* Initialize an empty simple registration request.
*/
static function build($required=null, $optional=null,
$policy_url=null,
$sreg_ns_uri=Auth_OpenID_SREG_NS_URI,
$cls='Auth_OpenID_SRegRequest')
{
$obj = new $cls();
$obj->required = array();
$obj->optional = array();
$obj->policy_url = $policy_url;
$obj->ns_uri = $sreg_ns_uri;
if ($required) {
if (!$obj->requestFields($required, true, true)) {
return null;
}
}
if ($optional) {
if (!$obj->requestFields($optional, false, true)) {
return null;
}
}
return $obj;
}
/**
* Create a simple registration request that contains the fields
* that were requested in the OpenID request with the given
* arguments
*
* $request: The OpenID authentication request from which to
* extract an sreg request.
*
* $cls: name of class to use when creating sreg request object.
* Used for testing.
*
* Returns the newly created simple registration request
*/
static function fromOpenIDRequest($request, $cls='Auth_OpenID_SRegRequest')
{
$obj = call_user_func_array(array($cls, 'build'),
array(null, null, null, Auth_OpenID_SREG_NS_URI, $cls));
// Since we're going to mess with namespace URI mapping, don't
// mutate the object that was passed in.
$m = $request->message;
$obj->ns_uri = $obj->_getSRegNS($m);
$args = $m->getArgs($obj->ns_uri);
if ($args === null || Auth_OpenID::isFailure($args)) {
return null;
}
$obj->parseExtensionArgs($args);
return $obj;
}
/**
* Parse the unqualified simple registration request parameters
* and add them to this object.
*
* This method is essentially the inverse of
* getExtensionArgs. This method restores the serialized simple
* registration request fields.
*
* If you are extracting arguments from a standard OpenID
* checkid_* request, you probably want to use fromOpenIDRequest,
* which will extract the sreg namespace and arguments from the
* OpenID request. This method is intended for cases where the
* OpenID server needs more control over how the arguments are
* parsed than that method provides.
*
* $args == $message->getArgs($ns_uri);
* $request->parseExtensionArgs($args);
*
* $args: The unqualified simple registration arguments
*
* strict: Whether requests with fields that are not defined in
* the simple registration specification should be tolerated (and
* ignored)
*/
function parseExtensionArgs($args, $strict=false)
{
foreach (array('required', 'optional') as $list_name) {
$required = ($list_name == 'required');
$items = Auth_OpenID::arrayGet($args, $list_name);
if ($items) {
foreach (explode(',', $items) as $field_name) {
if (!$this->requestField($field_name, $required, $strict)) {
if ($strict) {
return false;
}
}
}
}
}
$this->policy_url = Auth_OpenID::arrayGet($args, 'policy_url');
return true;
}
/**
* A list of all of the simple registration fields that were
* requested, whether they were required or optional.
*/
function allRequestedFields()
{
return array_merge($this->required, $this->optional);
}
/**
* Have any simple registration fields been requested?
*/
function wereFieldsRequested()
{
return count($this->allRequestedFields());
}
/**
* Was this field in the request?
*/
function contains($field_name)
{
return (in_array($field_name, $this->required) ||
in_array($field_name, $this->optional));
}
/**
* Request the specified field from the OpenID user
*
* $field_name: the unqualified simple registration field name
*
* required: whether the given field should be presented to the
* user as being a required to successfully complete the request
*
* strict: whether to raise an exception when a field is added to
* a request more than once
*/
function requestField($field_name,
$required=false, $strict=false)
{
if (!Auth_OpenID_checkFieldName($field_name)) {
return false;
}
if ($strict) {
if ($this->contains($field_name)) {
return false;
}
} else {
if (in_array($field_name, $this->required)) {
return true;
}
if (in_array($field_name, $this->optional)) {
if ($required) {
unset($this->optional[array_search($field_name,
$this->optional)]);
} else {
return true;
}
}
}
if ($required) {
$this->required[] = $field_name;
} else {
$this->optional[] = $field_name;
}
return true;
}
/**
* Add the given list of fields to the request
*
* field_names: The simple registration data fields to request
*
* required: Whether these values should be presented to the user
* as required
*
* strict: whether to raise an exception when a field is added to
* a request more than once
*/
function requestFields($field_names, $required=false, $strict=false)
{
if (!is_array($field_names)) {
return false;
}
foreach ($field_names as $field_name) {
if (!$this->requestField($field_name, $required, $strict=$strict)) {
return false;
}
}
return true;
}
/**
* Get a dictionary of unqualified simple registration arguments
* representing this request.
*
* This method is essentially the inverse of
* C{L{parseExtensionArgs}}. This method serializes the simple
* registration request fields.
*/
function getExtensionArgs()
{
$args = array();
if ($this->required) {
$args['required'] = implode(',', $this->required);
}
if ($this->optional) {
$args['optional'] = implode(',', $this->optional);
}
if ($this->policy_url) {
$args['policy_url'] = $this->policy_url;
}
return $args;
}
}
/**
* Represents the data returned in a simple registration response
* inside of an OpenID C{id_res} response. This object will be created
* by the OpenID server, added to the C{id_res} response object, and
* then extracted from the C{id_res} message by the Consumer.
*
* @package OpenID
*/
class Auth_OpenID_SRegResponse extends Auth_OpenID_SRegBase {
var $ns_alias = 'sreg';
function Auth_OpenID_SRegResponse($data=null,
$sreg_ns_uri=Auth_OpenID_SREG_NS_URI)
{
if ($data === null) {
$this->data = array();
} else {
$this->data = $data;
}
$this->ns_uri = $sreg_ns_uri;
}
/**
* Take a C{L{SRegRequest}} and a dictionary of simple
* registration values and create a C{L{SRegResponse}} object
* containing that data.
*
* request: The simple registration request object
*
* data: The simple registration data for this response, as a
* dictionary from unqualified simple registration field name to
* string (unicode) value. For instance, the nickname should be
* stored under the key 'nickname'.
*/
static function extractResponse($request, $data)
{
$obj = new Auth_OpenID_SRegResponse();
$obj->ns_uri = $request->ns_uri;
foreach ($request->allRequestedFields() as $field) {
$value = Auth_OpenID::arrayGet($data, $field);
if ($value !== null) {
$obj->data[$field] = $value;
}
}
return $obj;
}
/**
* Create a C{L{SRegResponse}} object from a successful OpenID
* library response
* (C{L{openid.consumer.consumer.SuccessResponse}}) response
* message
*
* success_response: A SuccessResponse from consumer.complete()
*
* signed_only: Whether to process only data that was
* signed in the id_res message from the server.
*
* Returns a simple registration response containing the data that
* was supplied with the C{id_res} response.
*/
static function fromSuccessResponse($success_response, $signed_only=true)
{
global $Auth_OpenID_sreg_data_fields;
$obj = new Auth_OpenID_SRegResponse();
$obj->ns_uri = $obj->_getSRegNS($success_response->message);
if ($signed_only) {
$args = $success_response->getSignedNS($obj->ns_uri);
} else {
$args = $success_response->message->getArgs($obj->ns_uri);
}
if ($args === null || Auth_OpenID::isFailure($args)) {
return null;
}
foreach ($Auth_OpenID_sreg_data_fields as $field_name => $desc) {
if (in_array($field_name, array_keys($args))) {
$obj->data[$field_name] = $args[$field_name];
}
}
return $obj;
}
function getExtensionArgs()
{
return $this->data;
}
// Read-only dictionary interface
function get($field_name, $default=null)
{
if (!Auth_OpenID_checkFieldName($field_name)) {
return null;
}
return Auth_OpenID::arrayGet($this->data, $field_name, $default);
}
function contents()
{
return $this->data;
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,36 +0,0 @@
<?php
/**
* OpenID Server Request
*
* @see Auth_OpenID_Server
*
* PHP versions 4 and 5
*
* LICENSE: See the COPYING file included in this distribution.
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005-2008 Janrain, Inc.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
*/
/**
* Imports
*/
require_once "Auth/OpenID.php";
/**
* Object that holds the state of a request to the OpenID server
*
* With accessor functions to get at the internal request data.
*
* @see Auth_OpenID_Server
* @package OpenID
*/
class Auth_OpenID_ServerRequest {
function Auth_OpenID_ServerRequest()
{
$this->mode = null;
}
}

View file

@ -1,461 +0,0 @@
<?php
/**
* Functions for dealing with OpenID trust roots
*
* PHP versions 4 and 5
*
* LICENSE: See the COPYING file included in this distribution.
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005-2008 Janrain, Inc.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
*/
require_once 'Auth/OpenID/Discover.php';
/**
* A regular expression that matches a domain ending in a top-level domains.
* Used in checking trust roots for sanity.
*
* @access private
*/
define('Auth_OpenID___TLDs',
'/\.(ac|ad|ae|aero|af|ag|ai|al|am|an|ao|aq|ar|arpa|as|asia' .
'|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|bi|biz|bj|bm|bn|bo|br' .
'|bs|bt|bv|bw|by|bz|ca|cat|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co' .
'|com|coop|cr|cu|cv|cx|cy|cz|de|dj|dk|dm|do|dz|ec|edu|ee|eg' .
'|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl' .
'|gm|gn|gov|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie' .
'|il|im|in|info|int|io|iq|ir|is|it|je|jm|jo|jobs|jp|ke|kg|kh' .
'|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly' .
'|ma|mc|md|me|mg|mh|mil|mk|ml|mm|mn|mo|mobi|mp|mq|mr|ms|mt' .
'|mu|museum|mv|mw|mx|my|mz|na|name|nc|ne|net|nf|ng|ni|nl|no' .
'|np|nr|nu|nz|om|org|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|pro|ps|pt' .
'|pw|py|qa|re|ro|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl' .
'|sm|sn|so|sr|st|su|sv|sy|sz|tc|td|tel|tf|tg|th|tj|tk|tl|tm' .
'|tn|to|tp|tr|travel|tt|tv|tw|tz|ua|ug|uk|us|uy|uz|va|vc|ve' .
'|vg|vi|vn|vu|wf|ws|xn--0zwm56d|xn--11b5bs3a9aj6g' .
'|xn--80akhbyknj4f|xn--9t4b11yi5a|xn--deba0ad|xn--g6w251d' .
'|xn--hgbk6aj7f53bba|xn--hlcj6aya9esc7a|xn--jxalpdlp' .
'|xn--kgbechtv|xn--zckzah|ye|yt|yu|za|zm|zw)\.?$/');
define('Auth_OpenID___HostSegmentRe',
"/^(?:[-a-zA-Z0-9!$&'\\(\\)\\*+,;=._~]|%[a-zA-Z0-9]{2})*$/");
/**
* A wrapper for trust-root related functions
*/
class Auth_OpenID_TrustRoot {
/*
* Return a discovery URL for this realm.
*
* Return null if the realm could not be parsed or was not valid.
*
* @param return_to The relying party return URL of the OpenID
* authentication request
*
* @return The URL upon which relying party discovery should be
* run in order to verify the return_to URL
*/
static function buildDiscoveryURL($realm)
{
$parsed = Auth_OpenID_TrustRoot::_parse($realm);
if ($parsed === false) {
return false;
}
if ($parsed['wildcard']) {
// Use "www." in place of the star
if ($parsed['host'][0] != '.') {
return false;
}
$www_domain = 'www' . $parsed['host'];
return sprintf('%s://%s%s', $parsed['scheme'],
$www_domain, $parsed['path']);
} else {
return $parsed['unparsed'];
}
}
/**
* Parse a URL into its trust_root parts.
*
* @static
*
* @access private
*
* @param string $trust_root The url to parse
*
* @return mixed $parsed Either an associative array of trust root
* parts or false if parsing failed.
*/
static function _parse($trust_root)
{
$trust_root = Auth_OpenID_urinorm($trust_root);
if ($trust_root === null) {
return false;
}
if (preg_match("/:\/\/[^:]+(:\d+){2,}(\/|$)/", $trust_root)) {
return false;
}
$parts = @parse_url($trust_root);
if ($parts === false) {
return false;
}
$required_parts = array('scheme', 'host');
$forbidden_parts = array('user', 'pass', 'fragment');
$keys = array_keys($parts);
if (array_intersect($keys, $required_parts) != $required_parts) {
return false;
}
if (array_intersect($keys, $forbidden_parts) != array()) {
return false;
}
if (!preg_match(Auth_OpenID___HostSegmentRe, $parts['host'])) {
return false;
}
$scheme = strtolower($parts['scheme']);
$allowed_schemes = array('http', 'https');
if (!in_array($scheme, $allowed_schemes)) {
return false;
}
$parts['scheme'] = $scheme;
$host = strtolower($parts['host']);
$hostparts = explode('*', $host);
switch (count($hostparts)) {
case 1:
$parts['wildcard'] = false;
break;
case 2:
if ($hostparts[0] ||
($hostparts[1] && substr($hostparts[1], 0, 1) != '.')) {
return false;
}
$host = $hostparts[1];
$parts['wildcard'] = true;
break;
default:
return false;
}
if (strpos($host, ':') !== false) {
return false;
}
$parts['host'] = $host;
if (isset($parts['path'])) {
$path = strtolower($parts['path']);
if (substr($path, 0, 1) != '/') {
return false;
}
} else {
$path = '/';
}
$parts['path'] = $path;
if (!isset($parts['port'])) {
$parts['port'] = false;
}
$parts['unparsed'] = $trust_root;
return $parts;
}
/**
* Is this trust root sane?
*
* A trust root is sane if it is syntactically valid and it has a
* reasonable domain name. Specifically, the domain name must be
* more than one level below a standard TLD or more than two
* levels below a two-letter tld.
*
* For example, '*.com' is not a sane trust root, but '*.foo.com'
* is. '*.co.uk' is not sane, but '*.bbc.co.uk' is.
*
* This check is not always correct, but it attempts to err on the
* side of marking sane trust roots insane instead of marking
* insane trust roots sane. For example, 'kink.fm' is marked as
* insane even though it "should" (for some meaning of should) be
* marked sane.
*
* This function should be used when creating OpenID servers to
* alert the users of the server when a consumer attempts to get
* the user to accept a suspicious trust root.
*
* @static
* @param string $trust_root The trust root to check
* @return bool $sanity Whether the trust root looks OK
*/
static function isSane($trust_root)
{
$parts = Auth_OpenID_TrustRoot::_parse($trust_root);
if ($parts === false) {
return false;
}
// Localhost is a special case
if ($parts['host'] == 'localhost') {
return true;
}
$host_parts = explode('.', $parts['host']);
if ($parts['wildcard']) {
// Remove the empty string from the beginning of the array
array_shift($host_parts);
}
if ($host_parts && !$host_parts[count($host_parts) - 1]) {
array_pop($host_parts);
}
if (!$host_parts) {
return false;
}
// Don't allow adjacent dots
if (in_array('', $host_parts, true)) {
return false;
}
// Get the top-level domain of the host. If it is not a valid TLD,
// it's not sane.
preg_match(Auth_OpenID___TLDs, $parts['host'], $matches);
if (!$matches) {
return false;
}
$tld = $matches[1];
if (count($host_parts) == 1) {
return false;
}
if ($parts['wildcard']) {
// It's a 2-letter tld with a short second to last segment
// so there needs to be more than two segments specified
// (e.g. *.co.uk is insane)
$second_level = $host_parts[count($host_parts) - 2];
if (strlen($tld) == 2 && strlen($second_level) <= 3) {
return count($host_parts) > 2;
}
}
return true;
}
/**
* Does this URL match the given trust root?
*
* Return whether the URL falls under the given trust root. This
* does not check whether the trust root is sane. If the URL or
* trust root do not parse, this function will return false.
*
* @param string $trust_root The trust root to match against
*
* @param string $url The URL to check
*
* @return bool $matches Whether the URL matches against the
* trust root
*/
static function match($trust_root, $url)
{
$trust_root_parsed = Auth_OpenID_TrustRoot::_parse($trust_root);
$url_parsed = Auth_OpenID_TrustRoot::_parse($url);
if (!$trust_root_parsed || !$url_parsed) {
return false;
}
// Check hosts matching
if ($url_parsed['wildcard']) {
return false;
}
if ($trust_root_parsed['wildcard']) {
$host_tail = $trust_root_parsed['host'];
$host = $url_parsed['host'];
if ($host_tail &&
substr($host, -(strlen($host_tail))) != $host_tail &&
substr($host_tail, 1) != $host) {
return false;
}
} else {
if ($trust_root_parsed['host'] != $url_parsed['host']) {
return false;
}
}
// Check path and query matching
$base_path = $trust_root_parsed['path'];
$path = $url_parsed['path'];
if (!isset($trust_root_parsed['query'])) {
if ($base_path != $path) {
if (substr($path, 0, strlen($base_path)) != $base_path) {
return false;
}
if (substr($base_path, strlen($base_path) - 1, 1) != '/' &&
substr($path, strlen($base_path), 1) != '/') {
return false;
}
}
} else {
$base_query = $trust_root_parsed['query'];
$query = @$url_parsed['query'];
$qplus = substr($query, 0, strlen($base_query) + 1);
$bqplus = $base_query . '&';
if ($base_path != $path ||
($base_query != $query && $qplus != $bqplus)) {
return false;
}
}
// The port and scheme need to match exactly
return ($trust_root_parsed['scheme'] == $url_parsed['scheme'] &&
$url_parsed['port'] === $trust_root_parsed['port']);
}
}
/*
* If the endpoint is a relying party OpenID return_to endpoint,
* return the endpoint URL. Otherwise, return None.
*
* This function is intended to be used as a filter for the Yadis
* filtering interface.
*
* @see: C{L{openid.yadis.services}}
* @see: C{L{openid.yadis.filters}}
*
* @param endpoint: An XRDS BasicServiceEndpoint, as returned by
* performing Yadis dicovery.
*
* @returns: The endpoint URL or None if the endpoint is not a
* relying party endpoint.
*/
function filter_extractReturnURL($endpoint)
{
if ($endpoint->matchTypes(array(Auth_OpenID_RP_RETURN_TO_URL_TYPE))) {
return $endpoint;
} else {
return null;
}
}
function &Auth_OpenID_extractReturnURL(&$endpoint_list)
{
$result = array();
foreach ($endpoint_list as $endpoint) {
if (filter_extractReturnURL($endpoint)) {
$result[] = $endpoint;
}
}
return $result;
}
/*
* Is the return_to URL under one of the supplied allowed return_to
* URLs?
*/
function Auth_OpenID_returnToMatches($allowed_return_to_urls, $return_to)
{
foreach ($allowed_return_to_urls as $allowed_return_to) {
// A return_to pattern works the same as a realm, except that
// it's not allowed to use a wildcard. We'll model this by
// parsing it as a realm, and not trying to match it if it has
// a wildcard.
$return_realm = Auth_OpenID_TrustRoot::_parse($allowed_return_to);
if (// Parses as a trust root
($return_realm !== false) &&
// Does not have a wildcard
(!$return_realm['wildcard']) &&
// Matches the return_to that we passed in with it
(Auth_OpenID_TrustRoot::match($allowed_return_to, $return_to))) {
return true;
}
}
// No URL in the list matched
return false;
}
/*
* Given a relying party discovery URL return a list of return_to
* URLs.
*/
function Auth_OpenID_getAllowedReturnURLs($relying_party_url, $fetcher,
$discover_function=null)
{
if ($discover_function === null) {
$discover_function = array('Auth_Yadis_Yadis', 'discover');
}
$xrds_parse_cb = array('Auth_OpenID_ServiceEndpoint', 'consumerFromXRDS');
list($rp_url_after_redirects, $endpoints) =
Auth_Yadis_getServiceEndpoints($relying_party_url, $xrds_parse_cb,
$discover_function, $fetcher);
if ($rp_url_after_redirects != $relying_party_url) {
// Verification caused a redirect
return false;
}
call_user_func_array($discover_function,
array($relying_party_url, $fetcher));
$return_to_urls = array();
$matching_endpoints = Auth_OpenID_extractReturnURL($endpoints);
foreach ($matching_endpoints as $e) {
$return_to_urls[] = $e->server_url;
}
return $return_to_urls;
}
/*
* Verify that a return_to URL is valid for the given realm.
*
* This function builds a discovery URL, performs Yadis discovery on
* it, makes sure that the URL does not redirect, parses out the
* return_to URLs, and finally checks to see if the current return_to
* URL matches the return_to.
*
* @return true if the return_to URL is valid for the realm
*/
function Auth_OpenID_verifyReturnTo($realm_str, $return_to, $fetcher,
$_vrfy='Auth_OpenID_getAllowedReturnURLs')
{
$disco_url = Auth_OpenID_TrustRoot::buildDiscoveryURL($realm_str);
if ($disco_url === false) {
return false;
}
$allowable_urls = call_user_func_array($_vrfy,
array($disco_url, $fetcher));
// The realm_str could not be parsed.
if ($allowable_urls === false) {
return false;
}
if (Auth_OpenID_returnToMatches($allowable_urls, $return_to)) {
return true;
} else {
return false;
}
}

View file

@ -1,249 +0,0 @@
<?php
/**
* URI normalization routines.
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005-2008 Janrain, Inc.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
*/
require_once 'Auth/Yadis/Misc.php';
// from appendix B of rfc 3986 (http://www.ietf.org/rfc/rfc3986.txt)
function Auth_OpenID_getURIPattern()
{
return '&^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?&';
}
function Auth_OpenID_getAuthorityPattern()
{
return '/^([^@]*@)?([^:]*)(:.*)?/';
}
function Auth_OpenID_getEncodedPattern()
{
return '/%([0-9A-Fa-f]{2})/';
}
# gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"
#
# sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
# / "*" / "+" / "," / ";" / "="
#
# unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
function Auth_OpenID_getURLIllegalCharRE()
{
return "/([^-A-Za-z0-9:\/\?#\[\]@\!\$&'\(\)\*\+,;=\._~\%])/";
}
function Auth_OpenID_getUnreserved()
{
$_unreserved = array();
for ($i = 0; $i < 256; $i++) {
$_unreserved[$i] = false;
}
for ($i = ord('A'); $i <= ord('Z'); $i++) {
$_unreserved[$i] = true;
}
for ($i = ord('0'); $i <= ord('9'); $i++) {
$_unreserved[$i] = true;
}
for ($i = ord('a'); $i <= ord('z'); $i++) {
$_unreserved[$i] = true;
}
$_unreserved[ord('-')] = true;
$_unreserved[ord('.')] = true;
$_unreserved[ord('_')] = true;
$_unreserved[ord('~')] = true;
return $_unreserved;
}
function Auth_OpenID_getEscapeRE()
{
$parts = array();
foreach (array_merge(Auth_Yadis_getUCSChars(),
Auth_Yadis_getIPrivateChars()) as $pair) {
list($m, $n) = $pair;
$parts[] = sprintf("%s-%s", chr($m), chr($n));
}
return sprintf('[%s]', implode('', $parts));
}
function Auth_OpenID_pct_encoded_replace_unreserved($mo)
{
$_unreserved = Auth_OpenID_getUnreserved();
$i = intval($mo[1], 16);
if ($_unreserved[$i]) {
return chr($i);
} else {
return strtoupper($mo[0]);
}
return $mo[0];
}
function Auth_OpenID_pct_encoded_replace($mo)
{
return chr(intval($mo[1], 16));
}
function Auth_OpenID_remove_dot_segments($path)
{
$result_segments = array();
while ($path) {
if (Auth_Yadis_startswith($path, '../')) {
$path = substr($path, 3);
} else if (Auth_Yadis_startswith($path, './')) {
$path = substr($path, 2);
} else if (Auth_Yadis_startswith($path, '/./')) {
$path = substr($path, 2);
} else if ($path == '/.') {
$path = '/';
} else if (Auth_Yadis_startswith($path, '/../')) {
$path = substr($path, 3);
if ($result_segments) {
array_pop($result_segments);
}
} else if ($path == '/..') {
$path = '/';
if ($result_segments) {
array_pop($result_segments);
}
} else if (($path == '..') ||
($path == '.')) {
$path = '';
} else {
$i = 0;
if ($path[0] == '/') {
$i = 1;
}
$i = strpos($path, '/', $i);
if ($i === false) {
$i = strlen($path);
}
$result_segments[] = substr($path, 0, $i);
$path = substr($path, $i);
}
}
return implode('', $result_segments);
}
function Auth_OpenID_urinorm($uri)
{
$uri_matches = array();
preg_match(Auth_OpenID_getURIPattern(), $uri, $uri_matches);
if (count($uri_matches) < 9) {
for ($i = count($uri_matches); $i <= 9; $i++) {
$uri_matches[] = '';
}
}
$illegal_matches = array();
preg_match(Auth_OpenID_getURLIllegalCharRE(),
$uri, $illegal_matches);
if ($illegal_matches) {
return null;
}
$scheme = $uri_matches[2];
if ($scheme) {
$scheme = strtolower($scheme);
}
$scheme = $uri_matches[2];
if ($scheme === '') {
// No scheme specified
return null;
}
$scheme = strtolower($scheme);
if (!in_array($scheme, array('http', 'https'))) {
// Not an absolute HTTP or HTTPS URI
return null;
}
$authority = $uri_matches[4];
if ($authority === '') {
// Not an absolute URI
return null;
}
$authority_matches = array();
preg_match(Auth_OpenID_getAuthorityPattern(),
$authority, $authority_matches);
if (count($authority_matches) === 0) {
// URI does not have a valid authority
return null;
}
if (count($authority_matches) < 4) {
for ($i = count($authority_matches); $i <= 4; $i++) {
$authority_matches[] = '';
}
}
list($_whole, $userinfo, $host, $port) = $authority_matches;
if ($userinfo === null) {
$userinfo = '';
}
if (strpos($host, '%') !== -1) {
$host = strtolower($host);
$host = preg_replace_callback(
Auth_OpenID_getEncodedPattern(),
'Auth_OpenID_pct_encoded_replace', $host);
// NO IDNA.
// $host = unicode($host, 'utf-8').encode('idna');
} else {
$host = strtolower($host);
}
if ($port) {
if (($port == ':') ||
($scheme == 'http' && $port == ':80') ||
($scheme == 'https' && $port == ':443')) {
$port = '';
}
} else {
$port = '';
}
$authority = $userinfo . $host . $port;
$path = $uri_matches[5];
$path = preg_replace_callback(
Auth_OpenID_getEncodedPattern(),
'Auth_OpenID_pct_encoded_replace_unreserved', $path);
$path = Auth_OpenID_remove_dot_segments($path);
if (!$path) {
$path = '/';
}
$query = $uri_matches[6];
if ($query === null) {
$query = '';
}
$fragment = $uri_matches[8];
if ($fragment === null) {
$fragment = '';
}
return $scheme . '://' . $authority . $path . $query . $fragment;
}

View file

@ -1,174 +0,0 @@
<?php
/**
* This module contains the HTTP fetcher interface
*
* PHP versions 4 and 5
*
* LICENSE: See the COPYING file included in this distribution.
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005-2008 Janrain, Inc.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
*/
/**
* Require logging functionality
*/
require_once "Auth/OpenID.php";
define('Auth_OpenID_FETCHER_MAX_RESPONSE_KB', 1024);
define('Auth_OpenID_USER_AGENT',
'php-openid/'.Auth_OpenID_VERSION.' (php/'.phpversion().')');
class Auth_Yadis_HTTPResponse {
function Auth_Yadis_HTTPResponse($final_url = null, $status = null,
$headers = null, $body = null)
{
$this->final_url = $final_url;
$this->status = $status;
$this->headers = $headers;
$this->body = $body;
}
}
/**
* This class is the interface for HTTP fetchers the Yadis library
* uses. This interface is only important if you need to write a new
* fetcher for some reason.
*
* @access private
* @package OpenID
*/
class Auth_Yadis_HTTPFetcher {
var $timeout = 20; // timeout in seconds.
/**
* Return whether a URL can be fetched. Returns false if the URL
* scheme is not allowed or is not supported by this fetcher
* implementation; returns true otherwise.
*
* @return bool
*/
function canFetchURL($url)
{
if ($this->isHTTPS($url) && !$this->supportsSSL()) {
Auth_OpenID::log("HTTPS URL unsupported fetching %s",
$url);
return false;
}
if (!$this->allowedURL($url)) {
Auth_OpenID::log("URL fetching not allowed for '%s'",
$url);
return false;
}
return true;
}
/**
* Return whether a URL should be allowed. Override this method to
* conform to your local policy.
*
* By default, will attempt to fetch any http or https URL.
*/
function allowedURL($url)
{
return $this->URLHasAllowedScheme($url);
}
/**
* Does this fetcher implementation (and runtime) support fetching
* HTTPS URLs? May inspect the runtime environment.
*
* @return bool $support True if this fetcher supports HTTPS
* fetching; false if not.
*/
function supportsSSL()
{
trigger_error("not implemented", E_USER_ERROR);
}
/**
* Is this an https URL?
*
* @access private
*/
function isHTTPS($url)
{
return (bool)preg_match('/^https:\/\//i', $url);
}
/**
* Is this an http or https URL?
*
* @access private
*/
function URLHasAllowedScheme($url)
{
return (bool)preg_match('/^https?:\/\//i', $url);
}
/**
* @access private
*/
function _findRedirect($headers, $url)
{
foreach ($headers as $line) {
if (strpos(strtolower($line), "location: ") === 0) {
$parts = explode(" ", $line, 2);
$loc = $parts[1];
$ppos = strpos($loc, "://");
if ($ppos === false || $ppos > strpos($loc, "/")) {
/* no host; add it */
$hpos = strpos($url, "://");
$prt = substr($url, 0, $hpos+3);
$url = substr($url, $hpos+3);
if (substr($loc, 0, 1) == "/") {
/* absolute path */
$fspos = strpos($url, "/");
if ($fspos) $loc = $prt.substr($url, 0, $fspos).$loc;
else $loc = $prt.$url.$loc;
} else {
/* relative path */
$pp = $prt;
while (1) {
$xpos = strpos($url, "/");
if ($xpos === false) break;
$apos = strpos($url, "?");
if ($apos !== false && $apos < $xpos) break;
$apos = strpos($url, "&");
if ($apos !== false && $apos < $xpos) break;
$pp .= substr($url, 0, $xpos+1);
$url = substr($url, $xpos+1);
}
$loc = $pp.$loc;
}
}
return $loc;
}
}
return null;
}
/**
* Fetches the specified URL using optional extra headers and
* returns the server's response.
*
* @param string $url The URL to be fetched.
* @param array $extra_headers An array of header strings
* (e.g. "Accept: text/html").
* @return mixed $result An array of ($code, $url, $headers,
* $body) if the URL could be fetched; null if the URL does not
* pass the URLHasAllowedScheme check or if the server's response
* is malformed.
*/
function get($url, $headers = null)
{
trigger_error("not implemented", E_USER_ERROR);
}
}

View file

@ -1,523 +0,0 @@
<?php
/**
* Yadis service manager to be used during yadis-driven authentication
* attempts.
*
* @package OpenID
*/
/**
* The base session class used by the Auth_Yadis_Manager. This
* class wraps the default PHP session machinery and should be
* subclassed if your application doesn't use PHP sessioning.
*
* @package OpenID
*/
class Auth_Yadis_PHPSession {
/**
* Set a session key/value pair.
*
* @param string $name The name of the session key to add.
* @param string $value The value to add to the session.
*/
function set($name, $value)
{
$_SESSION[$name] = $value;
}
/**
* Get a key's value from the session.
*
* @param string $name The name of the key to retrieve.
* @param string $default The optional value to return if the key
* is not found in the session.
* @return string $result The key's value in the session or
* $default if it isn't found.
*/
function get($name, $default=null)
{
if (isset($_SESSION) && array_key_exists($name, $_SESSION)) {
return $_SESSION[$name];
} else {
return $default;
}
}
/**
* Remove a key/value pair from the session.
*
* @param string $name The name of the key to remove.
*/
function del($name)
{
unset($_SESSION[$name]);
}
/**
* Return the contents of the session in array form.
*/
function contents()
{
return $_SESSION;
}
}
/**
* A session helper class designed to translate between arrays and
* objects. Note that the class used must have a constructor that
* takes no parameters. This is not a general solution, but it works
* for dumb objects that just need to have attributes set. The idea
* is that you'll subclass this and override $this->check($data) ->
* bool to implement your own session data validation.
*
* @package OpenID
*/
class Auth_Yadis_SessionLoader {
/**
* Override this.
*
* @access private
*/
function check($data)
{
return true;
}
/**
* Given a session data value (an array), this creates an object
* (returned by $this->newObject()) whose attributes and values
* are those in $data. Returns null if $data lacks keys found in
* $this->requiredKeys(). Returns null if $this->check($data)
* evaluates to false. Returns null if $this->newObject()
* evaluates to false.
*
* @access private
*/
function fromSession($data)
{
if (!$data) {
return null;
}
$required = $this->requiredKeys();
foreach ($required as $k) {
if (!array_key_exists($k, $data)) {
return null;
}
}
if (!$this->check($data)) {
return null;
}
$data = array_merge($data, $this->prepareForLoad($data));
$obj = $this->newObject($data);
if (!$obj) {
return null;
}
foreach ($required as $k) {
$obj->$k = $data[$k];
}
return $obj;
}
/**
* Prepares the data array by making any necessary changes.
* Returns an array whose keys and values will be used to update
* the original data array before calling $this->newObject($data).
*
* @access private
*/
function prepareForLoad($data)
{
return array();
}
/**
* Returns a new instance of this loader's class, using the
* session data to construct it if necessary. The object need
* only be created; $this->fromSession() will take care of setting
* the object's attributes.
*
* @access private
*/
function newObject($data)
{
return null;
}
/**
* Returns an array of keys and values built from the attributes
* of $obj. If $this->prepareForSave($obj) returns an array, its keys
* and values are used to update the $data array of attributes
* from $obj.
*
* @access private
*/
function toSession($obj)
{
$data = array();
foreach ($obj as $k => $v) {
$data[$k] = $v;
}
$extra = $this->prepareForSave($obj);
if ($extra && is_array($extra)) {
foreach ($extra as $k => $v) {
$data[$k] = $v;
}
}
return $data;
}
/**
* Override this.
*
* @access private
*/
function prepareForSave($obj)
{
return array();
}
}
/**
* A concrete loader implementation for Auth_OpenID_ServiceEndpoints.
*
* @package OpenID
*/
class Auth_OpenID_ServiceEndpointLoader extends Auth_Yadis_SessionLoader {
function newObject($data)
{
return new Auth_OpenID_ServiceEndpoint();
}
function requiredKeys()
{
$obj = new Auth_OpenID_ServiceEndpoint();
$data = array();
foreach ($obj as $k => $v) {
$data[] = $k;
}
return $data;
}
function check($data)
{
return is_array($data['type_uris']);
}
}
/**
* A concrete loader implementation for Auth_Yadis_Managers.
*
* @package OpenID
*/
class Auth_Yadis_ManagerLoader extends Auth_Yadis_SessionLoader {
function requiredKeys()
{
return array('starting_url',
'yadis_url',
'services',
'session_key',
'_current',
'stale');
}
function newObject($data)
{
return new Auth_Yadis_Manager($data['starting_url'],
$data['yadis_url'],
$data['services'],
$data['session_key']);
}
function check($data)
{
return is_array($data['services']);
}
function prepareForLoad($data)
{
$loader = new Auth_OpenID_ServiceEndpointLoader();
$services = array();
foreach ($data['services'] as $s) {
$services[] = $loader->fromSession($s);
}
return array('services' => $services);
}
function prepareForSave($obj)
{
$loader = new Auth_OpenID_ServiceEndpointLoader();
$services = array();
foreach ($obj->services as $s) {
$services[] = $loader->toSession($s);
}
return array('services' => $services);
}
}
/**
* The Yadis service manager which stores state in a session and
* iterates over <Service> elements in a Yadis XRDS document and lets
* a caller attempt to use each one. This is used by the Yadis
* library internally.
*
* @package OpenID
*/
class Auth_Yadis_Manager {
/**
* Intialize a new yadis service manager.
*
* @access private
*/
function Auth_Yadis_Manager($starting_url, $yadis_url,
$services, $session_key)
{
// The URL that was used to initiate the Yadis protocol
$this->starting_url = $starting_url;
// The URL after following redirects (the identifier)
$this->yadis_url = $yadis_url;
// List of service elements
$this->services = $services;
$this->session_key = $session_key;
// Reference to the current service object
$this->_current = null;
// Stale flag for cleanup if PHP lib has trouble.
$this->stale = false;
}
/**
* @access private
*/
function length()
{
// How many untried services remain?
return count($this->services);
}
/**
* Return the next service
*
* $this->current() will continue to return that service until the
* next call to this method.
*/
function nextService()
{
if ($this->services) {
$this->_current = array_shift($this->services);
} else {
$this->_current = null;
}
return $this->_current;
}
/**
* @access private
*/
function current()
{
// Return the current service.
// Returns None if there are no services left.
return $this->_current;
}
/**
* @access private
*/
function forURL($url)
{
return in_array($url, array($this->starting_url, $this->yadis_url));
}
/**
* @access private
*/
function started()
{
// Has the first service been returned?
return $this->_current !== null;
}
}
/**
* State management for discovery.
*
* High-level usage pattern is to call .getNextService(discover) in
* order to find the next available service for this user for this
* session. Once a request completes, call .cleanup() to clean up the
* session state.
*
* @package OpenID
*/
class Auth_Yadis_Discovery {
/**
* @access private
*/
var $DEFAULT_SUFFIX = 'auth';
/**
* @access private
*/
var $PREFIX = '_yadis_services_';
/**
* Initialize a discovery object.
*
* @param Auth_Yadis_PHPSession $session An object which
* implements the Auth_Yadis_PHPSession API.
* @param string $url The URL on which to attempt discovery.
* @param string $session_key_suffix The optional session key
* suffix override.
*/
function Auth_Yadis_Discovery($session, $url,
$session_key_suffix = null)
{
/// Initialize a discovery object
$this->session = $session;
$this->url = $url;
if ($session_key_suffix === null) {
$session_key_suffix = $this->DEFAULT_SUFFIX;
}
$this->session_key_suffix = $session_key_suffix;
$this->session_key = $this->PREFIX . $this->session_key_suffix;
}
/**
* Return the next authentication service for the pair of
* user_input and session. This function handles fallback.
*/
function getNextService($discover_cb, $fetcher)
{
$manager = $this->getManager();
if (!$manager || (!$manager->services)) {
$this->destroyManager();
list($yadis_url, $services) = call_user_func_array($discover_cb,
array(
$this->url,
$fetcher,
));
$manager = $this->createManager($services, $yadis_url);
}
if ($manager) {
$loader = new Auth_Yadis_ManagerLoader();
$service = $manager->nextService();
$this->session->set($this->session_key,
serialize($loader->toSession($manager)));
} else {
$service = null;
}
return $service;
}
/**
* Clean up Yadis-related services in the session and return the
* most-recently-attempted service from the manager, if one
* exists.
*
* @param $force True if the manager should be deleted regardless
* of whether it's a manager for $this->url.
*/
function cleanup($force=false)
{
$manager = $this->getManager($force);
if ($manager) {
$service = $manager->current();
$this->destroyManager($force);
} else {
$service = null;
}
return $service;
}
/**
* @access private
*/
function getSessionKey()
{
// Get the session key for this starting URL and suffix
return $this->PREFIX . $this->session_key_suffix;
}
/**
* @access private
*
* @param $force True if the manager should be returned regardless
* of whether it's a manager for $this->url.
*/
function getManager($force=false)
{
// Extract the YadisServiceManager for this object's URL and
// suffix from the session.
$manager_str = $this->session->get($this->getSessionKey());
$manager = null;
if ($manager_str !== null) {
$loader = new Auth_Yadis_ManagerLoader();
$manager = $loader->fromSession(unserialize($manager_str));
}
if ($manager && ($manager->forURL($this->url) || $force)) {
return $manager;
}
}
/**
* @access private
*/
function createManager($services, $yadis_url = null)
{
$key = $this->getSessionKey();
if ($this->getManager()) {
return $this->getManager();
}
if ($services) {
$loader = new Auth_Yadis_ManagerLoader();
$manager = new Auth_Yadis_Manager($this->url, $yadis_url,
$services, $key);
$this->session->set($this->session_key,
serialize($loader->toSession($manager)));
return $manager;
}
}
/**
* @access private
*
* @param $force True if the manager should be deleted regardless
* of whether it's a manager for $this->url.
*/
function destroyManager($force=false)
{
if ($this->getManager($force) !== null) {
$key = $this->getSessionKey();
$this->session->del($key);
}
}
}

View file

@ -1,58 +0,0 @@
<?php
/**
* Miscellaneous utility values and functions for OpenID and Yadis.
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005-2008 Janrain, Inc.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
*/
function Auth_Yadis_getUCSChars()
{
return array(
array(0xA0, 0xD7FF),
array(0xF900, 0xFDCF),
array(0xFDF0, 0xFFEF),
array(0x10000, 0x1FFFD),
array(0x20000, 0x2FFFD),
array(0x30000, 0x3FFFD),
array(0x40000, 0x4FFFD),
array(0x50000, 0x5FFFD),
array(0x60000, 0x6FFFD),
array(0x70000, 0x7FFFD),
array(0x80000, 0x8FFFD),
array(0x90000, 0x9FFFD),
array(0xA0000, 0xAFFFD),
array(0xB0000, 0xBFFFD),
array(0xC0000, 0xCFFFD),
array(0xD0000, 0xDFFFD),
array(0xE1000, 0xEFFFD)
);
}
function Auth_Yadis_getIPrivateChars()
{
return array(
array(0xE000, 0xF8FF),
array(0xF0000, 0xFFFFD),
array(0x100000, 0x10FFFD)
);
}
function Auth_Yadis_pct_escape_unicode($char_match)
{
$c = $char_match[0];
$result = "";
for ($i = 0; $i < strlen($c); $i++) {
$result .= "%".sprintf("%X", ord($c[$i]));
}
return $result;
}
function Auth_Yadis_startswith($s, $stuff)
{
return strpos($s, $stuff) === 0;
}

View file

@ -1,266 +0,0 @@
<?php
/**
* This module contains the CURL-based HTTP fetcher implementation.
*
* PHP versions 4 and 5
*
* LICENSE: See the COPYING file included in this distribution.
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005-2008 Janrain, Inc.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
*/
/**
* Interface import
*/
require_once "Auth/Yadis/HTTPFetcher.php";
require_once "Auth/OpenID.php";
/**
* A paranoid {@link Auth_Yadis_HTTPFetcher} class which uses CURL
* for fetching.
*
* @package OpenID
*/
class Auth_Yadis_ParanoidHTTPFetcher extends Auth_Yadis_HTTPFetcher {
function Auth_Yadis_ParanoidHTTPFetcher()
{
$this->reset();
}
function reset()
{
$this->headers = array();
$this->data = "";
}
/**
* @access private
*/
function _writeHeader($ch, $header)
{
array_push($this->headers, rtrim($header));
return strlen($header);
}
/**
* @access private
*/
function _writeData($ch, $data)
{
if (strlen($this->data) > 1024*Auth_OpenID_FETCHER_MAX_RESPONSE_KB) {
return 0;
} else {
$this->data .= $data;
return strlen($data);
}
}
/**
* Does this fetcher support SSL URLs?
*/
function supportsSSL()
{
$v = curl_version();
if(is_array($v)) {
return in_array('https', $v['protocols']);
} elseif (is_string($v)) {
return preg_match('/OpenSSL/i', $v);
} else {
return 0;
}
}
function get($url, $extra_headers = null)
{
if (!$this->canFetchURL($url)) {
return null;
}
$stop = time() + $this->timeout;
$off = $this->timeout;
$redir = true;
while ($redir && ($off > 0)) {
$this->reset();
$c = curl_init();
if ($c === false) {
Auth_OpenID::log(
"curl_init returned false; could not " .
"initialize for URL '%s'", $url);
return null;
}
if (defined('CURLOPT_NOSIGNAL')) {
curl_setopt($c, CURLOPT_NOSIGNAL, true);
}
if (!$this->allowedURL($url)) {
Auth_OpenID::log("Fetching URL not allowed: %s",
$url);
return null;
}
curl_setopt($c, CURLOPT_WRITEFUNCTION,
array($this, "_writeData"));
curl_setopt($c, CURLOPT_HEADERFUNCTION,
array($this, "_writeHeader"));
if ($extra_headers) {
curl_setopt($c, CURLOPT_HTTPHEADER, $extra_headers);
}
$cv = curl_version();
if(is_array($cv)) {
$curl_user_agent = 'curl/'.$cv['version'];
} else {
$curl_user_agent = $cv;
}
curl_setopt($c, CURLOPT_USERAGENT,
Auth_OpenID_USER_AGENT.' '.$curl_user_agent);
curl_setopt($c, CURLOPT_TIMEOUT, $off);
curl_setopt($c, CURLOPT_URL, $url);
if (defined('Auth_OpenID_VERIFY_HOST')) {
// set SSL verification options only if Auth_OpenID_VERIFY_HOST
// is explicitly set, otherwise use system default.
if (Auth_OpenID_VERIFY_HOST) {
curl_setopt($c, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($c, CURLOPT_SSL_VERIFYHOST, 2);
if (defined('Auth_OpenID_CAINFO')) {
curl_setopt($c, CURLOPT_CAINFO, Auth_OpenID_CAINFO);
}
} else {
curl_setopt($c, CURLOPT_SSL_VERIFYPEER, false);
}
}
if (defined('Auth_OpenID_HTTP_PROXY')) {
curl_setopt($c, CURLOPT_PROXY, Auth_OpenID_HTTP_PROXY);
}
curl_exec($c);
$code = curl_getinfo($c, CURLINFO_HTTP_CODE);
$body = $this->data;
$headers = $this->headers;
if (!$code) {
Auth_OpenID::log("Got no response code when fetching %s", $url);
Auth_OpenID::log("CURL error (%s): %s",
curl_errno($c), curl_error($c));
return null;
}
if (in_array($code, array(301, 302, 303, 307))) {
$url = $this->_findRedirect($headers, $url);
$redir = true;
} else {
$redir = false;
curl_close($c);
if (defined('Auth_OpenID_VERIFY_HOST') &&
Auth_OpenID_VERIFY_HOST == true &&
$this->isHTTPS($url)) {
Auth_OpenID::log('OpenID: Verified SSL host %s using '.
'curl/get', $url);
}
$new_headers = array();
foreach ($headers as $header) {
if (strpos($header, ': ')) {
list($name, $value) = explode(': ', $header, 2);
$new_headers[$name] = $value;
}
}
return new Auth_Yadis_HTTPResponse($url, $code,
$new_headers, $body);
}
$off = $stop - time();
}
return null;
}
function post($url, $body, $extra_headers = null)
{
if (!$this->canFetchURL($url)) {
return null;
}
$this->reset();
$c = curl_init();
if (defined('CURLOPT_NOSIGNAL')) {
curl_setopt($c, CURLOPT_NOSIGNAL, true);
}
if (defined('Auth_OpenID_HTTP_PROXY')) {
curl_setopt($c, CURLOPT_PROXY, Auth_OpenID_HTTP_PROXY);
}
curl_setopt($c, CURLOPT_POST, true);
curl_setopt($c, CURLOPT_POSTFIELDS, $body);
curl_setopt($c, CURLOPT_TIMEOUT, $this->timeout);
curl_setopt($c, CURLOPT_URL, $url);
curl_setopt($c, CURLOPT_WRITEFUNCTION,
array($this, "_writeData"));
if (defined('Auth_OpenID_VERIFY_HOST')) {
// set SSL verification options only if Auth_OpenID_VERIFY_HOST
// is explicitly set, otherwise use system default.
if (Auth_OpenID_VERIFY_HOST) {
curl_setopt($c, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($c, CURLOPT_SSL_VERIFYHOST, 2);
if (defined('Auth_OpenID_CAINFO')) {
curl_setopt($c, CURLOPT_CAINFO, Auth_OpenID_CAINFO);
}
} else {
curl_setopt($c, CURLOPT_SSL_VERIFYPEER, false);
}
}
curl_exec($c);
$code = curl_getinfo($c, CURLINFO_HTTP_CODE);
if (!$code) {
Auth_OpenID::log("Got no response code when fetching %s", $url);
Auth_OpenID::log("CURL error (%s): %s",
curl_errno($c), curl_error($c));
return null;
}
if (defined('Auth_OpenID_VERIFY_HOST') &&
Auth_OpenID_VERIFY_HOST == true &&
$this->isHTTPS($url)) {
Auth_OpenID::log('OpenID: Verified SSL host %s using '.
'curl/post', $url);
}
$body = $this->data;
curl_close($c);
$new_headers = $extra_headers;
foreach ($this->headers as $header) {
if (strpos($header, ': ')) {
list($name, $value) = explode(': ', $header, 2);
$new_headers[$name] = $value;
}
}
return new Auth_Yadis_HTTPResponse($url, $code,
$new_headers, $body);
}
}

View file

@ -1,235 +0,0 @@
<?php
/**
* This is the HTML pseudo-parser for the Yadis library.
*
* PHP versions 4 and 5
*
* LICENSE: See the COPYING file included in this distribution.
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005-2008 Janrain, Inc.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
*/
/**
* This class is responsible for scanning an HTML string to find META
* tags and their attributes. This is used by the Yadis discovery
* process. This class must be instantiated to be used.
*
* @package OpenID
*/
class Auth_Yadis_ParseHTML {
/**
* @access private
*/
var $_re_flags = "si";
/**
* @access private
*/
var $_removed_re =
"<!--.*?-->|<!\[CDATA\[.*?\]\]>|<script\b(?!:)[^>]*>.*?<\/script>";
/**
* @access private
*/
var $_tag_expr = "<%s%s(?:\s.*?)?%s>";
/**
* @access private
*/
var $_attr_find = '\b([-\w]+)=(".*?"|\'.*?\'|.+?)[\/\s>]';
function Auth_Yadis_ParseHTML()
{
$this->_attr_find = sprintf("/%s/%s",
$this->_attr_find,
$this->_re_flags);
$this->_removed_re = sprintf("/%s/%s",
$this->_removed_re,
$this->_re_flags);
$this->_entity_replacements = array(
'amp' => '&',
'lt' => '<',
'gt' => '>',
'quot' => '"'
);
$this->_ent_replace =
sprintf("&(%s);", implode("|",
$this->_entity_replacements));
}
/**
* Strip single and double quotes off of a string, if they are
* present.
*
* @access private
* @param string $str The original string
* @return string $new_str The new string with leading and
* trailing quotes removed
*/
function removeQuotes($str)
{
$matches = array();
$double = '/^"(.*)"$/';
$single = "/^\'(.*)\'$/";
if (preg_match($double, $str, $matches)) {
return $matches[1];
} else if (preg_match($single, $str, $matches)) {
return $matches[1];
} else {
return $str;
}
}
/**
* Create a regular expression that will match an opening
* or closing tag from a set of names.
*
* @access private
* @param mixed $tag_names Tag names to match
* @param mixed $close false/0 = no, true/1 = yes, other = maybe
* @param mixed $self_close false/0 = no, true/1 = yes, other = maybe
* @return string $regex A regular expression string to be used
* in, say, preg_match.
*/
function tagPattern($tag_names, $close, $self_close)
{
if (is_array($tag_names)) {
$tag_names = '(?:'.implode('|',$tag_names).')';
}
if ($close) {
$close = '\/' . (($close == 1)? '' : '?');
} else {
$close = '';
}
if ($self_close) {
$self_close = '(?:\/\s*)' . (($self_close == 1)? '' : '?');
} else {
$self_close = '';
}
$expr = sprintf($this->_tag_expr, $close, $tag_names, $self_close);
return sprintf("/%s/%s", $expr, $this->_re_flags);
}
/**
* Given an HTML document string, this finds all the META tags in
* the document, provided they are found in the
* <HTML><HEAD>...</HEAD> section of the document. The <HTML> tag
* may be missing.
*
* @access private
* @param string $html_string An HTMl document string
* @return array $tag_list Array of tags; each tag is an array of
* attribute -> value.
*/
function getMetaTags($html_string)
{
$html_string = preg_replace($this->_removed_re,
"",
$html_string);
$key_tags = array($this->tagPattern('html', false, false),
$this->tagPattern('head', false, false),
$this->tagPattern('head', true, false),
$this->tagPattern('html', true, false),
$this->tagPattern(array(
'body', 'frameset', 'frame', 'p', 'div',
'table','span','a'), 'maybe', 'maybe'));
$key_tags_pos = array();
foreach ($key_tags as $pat) {
$matches = array();
preg_match($pat, $html_string, $matches, PREG_OFFSET_CAPTURE);
if($matches) {
$key_tags_pos[] = $matches[0][1];
} else {
$key_tags_pos[] = null;
}
}
// no opening head tag
if (is_null($key_tags_pos[1])) {
return array();
}
// the effective </head> is the min of the following
if (is_null($key_tags_pos[2])) {
$key_tags_pos[2] = strlen($html_string);
}
foreach (array($key_tags_pos[3], $key_tags_pos[4]) as $pos) {
if (!is_null($pos) && $pos < $key_tags_pos[2]) {
$key_tags_pos[2] = $pos;
}
}
// closing head tag comes before opening head tag
if ($key_tags_pos[1] > $key_tags_pos[2]) {
return array();
}
// if there is an opening html tag, make sure the opening head tag
// comes after it
if (!is_null($key_tags_pos[0]) && $key_tags_pos[1] < $key_tags_pos[0]) {
return array();
}
$html_string = substr($html_string, $key_tags_pos[1],
($key_tags_pos[2]-$key_tags_pos[1]));
$link_data = array();
$link_matches = array();
if (!preg_match_all($this->tagPattern('meta', false, 'maybe'),
$html_string, $link_matches)) {
return array();
}
foreach ($link_matches[0] as $link) {
$attr_matches = array();
preg_match_all($this->_attr_find, $link, $attr_matches);
$link_attrs = array();
foreach ($attr_matches[0] as $index => $full_match) {
$name = $attr_matches[1][$index];
$value = html_entity_decode(
$this->removeQuotes($attr_matches[2][$index]));
$link_attrs[strtolower($name)] = $value;
}
$link_data[] = $link_attrs;
}
return $link_data;
}
/**
* Looks for a META tag with an "http-equiv" attribute whose value
* is one of ("x-xrds-location", "x-yadis-location"), ignoring
* case. If such a META tag is found, its "content" attribute
* value is returned.
*
* @param string $html_string An HTML document in string format
* @return mixed $content The "content" attribute value of the
* META tag, if found, or null if no such tag was found.
*/
function getHTTPEquiv($html_string)
{
$meta_tags = $this->getMetaTags($html_string);
if ($meta_tags) {
foreach ($meta_tags as $tag) {
if (array_key_exists('http-equiv', $tag) &&
(in_array(strtolower($tag['http-equiv']),
array('x-xrds-location', 'x-yadis-location'))) &&
array_key_exists('content', $tag)) {
return $tag['content'];
}
}
}
return null;
}
}

View file

@ -1,248 +0,0 @@
<?php
/**
* This module contains the plain non-curl HTTP fetcher
* implementation.
*
* PHP versions 4 and 5
*
* LICENSE: See the COPYING file included in this distribution.
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005-2008 Janrain, Inc.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
*/
/**
* Interface import
*/
require_once "Auth/Yadis/HTTPFetcher.php";
/**
* This class implements a plain, hand-built socket-based fetcher
* which will be used in the event that CURL is unavailable.
*
* @package OpenID
*/
class Auth_Yadis_PlainHTTPFetcher extends Auth_Yadis_HTTPFetcher {
/**
* Does this fetcher support SSL URLs?
*/
function supportsSSL()
{
return function_exists('openssl_open');
}
function get($url, $extra_headers = null)
{
if (!$this->canFetchURL($url)) {
return null;
}
$redir = true;
$stop = time() + $this->timeout;
$off = $this->timeout;
while ($redir && ($off > 0)) {
$parts = parse_url($url);
$specify_port = true;
// Set a default port.
if (!array_key_exists('port', $parts)) {
$specify_port = false;
if ($parts['scheme'] == 'http') {
$parts['port'] = 80;
} elseif ($parts['scheme'] == 'https') {
$parts['port'] = 443;
} else {
return null;
}
}
if (!array_key_exists('path', $parts)) {
$parts['path'] = '/';
}
$host = $parts['host'];
if ($parts['scheme'] == 'https') {
$host = 'ssl://' . $host;
}
$user_agent = Auth_OpenID_USER_AGENT;
$headers = array(
"GET ".$parts['path'].
(array_key_exists('query', $parts) ?
"?".$parts['query'] : "").
" HTTP/1.0",
"User-Agent: $user_agent",
"Host: ".$parts['host'].
($specify_port ? ":".$parts['port'] : ""),
"Port: ".$parts['port']);
$errno = 0;
$errstr = '';
if ($extra_headers) {
foreach ($extra_headers as $h) {
$headers[] = $h;
}
}
@$sock = fsockopen($host, $parts['port'], $errno, $errstr,
$this->timeout);
if ($sock === false) {
return false;
}
stream_set_timeout($sock, $this->timeout);
fputs($sock, implode("\r\n", $headers) . "\r\n\r\n");
$data = "";
$kilobytes = 0;
while (!feof($sock) &&
$kilobytes < Auth_OpenID_FETCHER_MAX_RESPONSE_KB ) {
$data .= fgets($sock, 1024);
$kilobytes += 1;
}
fclose($sock);
// Split response into header and body sections
list($headers, $body) = explode("\r\n\r\n", $data, 2);
$headers = explode("\r\n", $headers);
$http_code = explode(" ", $headers[0]);
$code = $http_code[1];
if (in_array($code, array('301', '302'))) {
$url = $this->_findRedirect($headers, $url);
$redir = true;
} else {
$redir = false;
}
$off = $stop - time();
}
$new_headers = array();
foreach ($headers as $header) {
if (preg_match("/:/", $header)) {
$parts = explode(": ", $header, 2);
if (count($parts) == 2) {
list($name, $value) = $parts;
$new_headers[$name] = $value;
}
}
}
return new Auth_Yadis_HTTPResponse($url, $code, $new_headers, $body);
}
function post($url, $body, $extra_headers = null)
{
if (!$this->canFetchURL($url)) {
return null;
}
$parts = parse_url($url);
$headers = array();
$post_path = $parts['path'];
if (isset($parts['query'])) {
$post_path .= '?' . $parts['query'];
}
$headers[] = "POST ".$post_path." HTTP/1.0";
$headers[] = "Host: " . $parts['host'];
$headers[] = "Content-type: application/x-www-form-urlencoded";
$headers[] = "Content-length: " . strval(strlen($body));
if ($extra_headers &&
is_array($extra_headers)) {
$headers = array_merge($headers, $extra_headers);
}
// Join all headers together.
$all_headers = implode("\r\n", $headers);
// Add headers, two newlines, and request body.
$request = $all_headers . "\r\n\r\n" . $body;
// Set a default port.
if (!array_key_exists('port', $parts)) {
if ($parts['scheme'] == 'http') {
$parts['port'] = 80;
} elseif ($parts['scheme'] == 'https') {
$parts['port'] = 443;
} else {
return null;
}
}
if ($parts['scheme'] == 'https') {
$parts['host'] = sprintf("ssl://%s", $parts['host']);
}
// Connect to the remote server.
$errno = 0;
$errstr = '';
$sock = fsockopen($parts['host'], $parts['port'], $errno, $errstr,
$this->timeout);
if ($sock === false) {
return null;
}
stream_set_timeout($sock, $this->timeout);
// Write the POST request.
fputs($sock, $request);
// Get the response from the server.
$response = "";
while (!feof($sock)) {
if ($data = fgets($sock, 128)) {
$response .= $data;
} else {
break;
}
}
// Split the request into headers and body.
list($headers, $response_body) = explode("\r\n\r\n", $response, 2);
$headers = explode("\r\n", $headers);
// Expect the first line of the headers data to be something
// like HTTP/1.1 200 OK. Split the line on spaces and take
// the second token, which should be the return code.
$http_code = explode(" ", $headers[0]);
$code = $http_code[1];
$new_headers = array();
foreach ($headers as $header) {
if (preg_match("/:/", $header)) {
list($name, $value) = explode(": ", $header, 2);
$new_headers[$name] = $value;
}
}
return new Auth_Yadis_HTTPResponse($url, $code,
$new_headers, $response_body);
}
}

View file

@ -1,364 +0,0 @@
<?php
/**
* XML-parsing classes to wrap the domxml and DOM extensions for PHP 4
* and 5, respectively.
*
* @package OpenID
*/
/**
* The base class for wrappers for available PHP XML-parsing
* extensions. To work with this Yadis library, subclasses of this
* class MUST implement the API as defined in the remarks for this
* class. Subclasses of Auth_Yadis_XMLParser are used to wrap
* particular PHP XML extensions such as 'domxml'. These are used
* internally by the library depending on the availability of
* supported PHP XML extensions.
*
* @package OpenID
*/
class Auth_Yadis_XMLParser {
/**
* Initialize an instance of Auth_Yadis_XMLParser with some
* XML and namespaces. This SHOULD NOT be overridden by
* subclasses.
*
* @param string $xml_string A string of XML to be parsed.
* @param array $namespace_map An array of ($ns_name => $ns_uri)
* to be registered with the XML parser. May be empty.
* @return boolean $result True if the initialization and
* namespace registration(s) succeeded; false otherwise.
*/
function init($xml_string, $namespace_map)
{
if (!$this->setXML($xml_string)) {
return false;
}
foreach ($namespace_map as $prefix => $uri) {
if (!$this->registerNamespace($prefix, $uri)) {
return false;
}
}
return true;
}
/**
* Register a namespace with the XML parser. This should be
* overridden by subclasses.
*
* @param string $prefix The namespace prefix to appear in XML tag
* names.
*
* @param string $uri The namespace URI to be used to identify the
* namespace in the XML.
*
* @return boolean $result True if the registration succeeded;
* false otherwise.
*/
function registerNamespace($prefix, $uri)
{
// Not implemented.
}
/**
* Set this parser object's XML payload. This should be
* overridden by subclasses.
*
* @param string $xml_string The XML string to pass to this
* object's XML parser.
*
* @return boolean $result True if the initialization succeeded;
* false otherwise.
*/
function setXML($xml_string)
{
// Not implemented.
}
/**
* Evaluate an XPath expression and return the resulting node
* list. This should be overridden by subclasses.
*
* @param string $xpath The XPath expression to be evaluated.
*
* @param mixed $node A node object resulting from a previous
* evalXPath call. This node, if specified, provides the context
* for the evaluation of this xpath expression.
*
* @return array $node_list An array of matching opaque node
* objects to be used with other methods of this parser class.
*/
function &evalXPath($xpath, $node = null)
{
// Not implemented.
}
/**
* Return the textual content of a specified node.
*
* @param mixed $node A node object from a previous call to
* $this->evalXPath().
*
* @return string $content The content of this node.
*/
function content($node)
{
// Not implemented.
}
/**
* Return the attributes of a specified node.
*
* @param mixed $node A node object from a previous call to
* $this->evalXPath().
*
* @return array $attrs An array mapping attribute names to
* values.
*/
function attributes($node)
{
// Not implemented.
}
}
/**
* This concrete implementation of Auth_Yadis_XMLParser implements
* the appropriate API for the 'domxml' extension which is typically
* packaged with PHP 4. This class will be used whenever the 'domxml'
* extension is detected. See the Auth_Yadis_XMLParser class for
* details on this class's methods.
*
* @package OpenID
*/
class Auth_Yadis_domxml extends Auth_Yadis_XMLParser {
function Auth_Yadis_domxml()
{
$this->xml = null;
$this->doc = null;
$this->xpath = null;
$this->errors = array();
}
function setXML($xml_string)
{
$this->xml = $xml_string;
$this->doc = @domxml_open_mem($xml_string, DOMXML_LOAD_PARSING,
$this->errors);
if (!$this->doc) {
return false;
}
$this->xpath = $this->doc->xpath_new_context();
return true;
}
function registerNamespace($prefix, $uri)
{
return xpath_register_ns($this->xpath, $prefix, $uri);
}
function &evalXPath($xpath, $node = null)
{
if ($node) {
$result = @$this->xpath->xpath_eval($xpath, $node);
} else {
$result = @$this->xpath->xpath_eval($xpath);
}
if (!$result) {
$n = array();
return $n;
}
if (!$result->nodeset) {
$n = array();
return $n;
}
return $result->nodeset;
}
function content($node)
{
if ($node) {
return $node->get_content();
}
}
function attributes($node)
{
if ($node) {
$arr = $node->attributes();
$result = array();
if ($arr) {
foreach ($arr as $attrnode) {
$result[$attrnode->name] = $attrnode->value;
}
}
return $result;
}
}
}
/**
* This concrete implementation of Auth_Yadis_XMLParser implements
* the appropriate API for the 'dom' extension which is typically
* packaged with PHP 5. This class will be used whenever the 'dom'
* extension is detected. See the Auth_Yadis_XMLParser class for
* details on this class's methods.
*
* @package OpenID
*/
class Auth_Yadis_dom extends Auth_Yadis_XMLParser {
function Auth_Yadis_dom()
{
$this->xml = null;
$this->doc = null;
$this->xpath = null;
$this->errors = array();
}
function setXML($xml_string)
{
$this->xml = $xml_string;
$this->doc = new DOMDocument;
if (!$this->doc) {
return false;
}
// libxml_disable_entity_loader (PHP 5 >= 5.2.11)
if (function_exists('libxml_disable_entity_loader') && function_exists('libxml_use_internal_errors')) {
// disable external entities and libxml errors
$loader = libxml_disable_entity_loader(true);
$errors = libxml_use_internal_errors(true);
$parse_result = @$this->doc->loadXML($xml_string);
libxml_disable_entity_loader($loader);
libxml_use_internal_errors($errors);
} else {
$parse_result = @$this->doc->loadXML($xml_string);
}
if (!$parse_result) {
return false;
}
$this->xpath = new DOMXPath($this->doc);
if ($this->xpath) {
return true;
} else {
return false;
}
}
function registerNamespace($prefix, $uri)
{
return $this->xpath->registerNamespace($prefix, $uri);
}
function &evalXPath($xpath, $node = null)
{
if ($node) {
$result = @$this->xpath->query($xpath, $node);
} else {
$result = @$this->xpath->query($xpath);
}
$n = array();
if (!$result) {
return $n;
}
for ($i = 0; $i < $result->length; $i++) {
$n[] = $result->item($i);
}
return $n;
}
function content($node)
{
if ($node) {
return $node->textContent;
}
}
function attributes($node)
{
if ($node) {
$arr = $node->attributes;
$result = array();
if ($arr) {
for ($i = 0; $i < $arr->length; $i++) {
$node = $arr->item($i);
$result[$node->nodeName] = $node->nodeValue;
}
}
return $result;
}
}
}
global $__Auth_Yadis_defaultParser;
$__Auth_Yadis_defaultParser = null;
/**
* Set a default parser to override the extension-driven selection of
* available parser classes. This is helpful in a test environment or
* one in which multiple parsers can be used but one is more
* desirable.
*
* @param Auth_Yadis_XMLParser $parser An instance of a
* Auth_Yadis_XMLParser subclass.
*/
function Auth_Yadis_setDefaultParser($parser)
{
global $__Auth_Yadis_defaultParser;
$__Auth_Yadis_defaultParser = $parser;
}
function Auth_Yadis_getSupportedExtensions()
{
return array('dom' => 'Auth_Yadis_dom',
'domxml' => 'Auth_Yadis_domxml');
}
/**
* Returns an instance of a Auth_Yadis_XMLParser subclass based on
* the availability of PHP extensions for XML parsing. If
* Auth_Yadis_setDefaultParser has been called, the parser used in
* that call will be returned instead.
*/
function Auth_Yadis_getXMLParser()
{
global $__Auth_Yadis_defaultParser;
if (isset($__Auth_Yadis_defaultParser)) {
return $__Auth_Yadis_defaultParser;
}
foreach(Auth_Yadis_getSupportedExtensions() as $extension => $classname)
{
if (extension_loaded($extension))
{
$p = new $classname();
Auth_Yadis_setDefaultParser($p);
return $p;
}
}
return false;
}

View file

@ -1,478 +0,0 @@
<?php
/**
* This module contains the XRDS parsing code.
*
* PHP versions 4 and 5
*
* LICENSE: See the COPYING file included in this distribution.
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005-2008 Janrain, Inc.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
*/
/**
* Require the XPath implementation.
*/
require_once 'Auth/Yadis/XML.php';
/**
* This match mode means a given service must match ALL filters passed
* to the Auth_Yadis_XRDS::services() call.
*/
define('SERVICES_YADIS_MATCH_ALL', 101);
/**
* This match mode means a given service must match ANY filters (at
* least one) passed to the Auth_Yadis_XRDS::services() call.
*/
define('SERVICES_YADIS_MATCH_ANY', 102);
/**
* The priority value used for service elements with no priority
* specified.
*/
define('SERVICES_YADIS_MAX_PRIORITY', pow(2, 30));
/**
* XRD XML namespace
*/
define('Auth_Yadis_XMLNS_XRD_2_0', 'xri://$xrd*($v*2.0)');
/**
* XRDS XML namespace
*/
define('Auth_Yadis_XMLNS_XRDS', 'xri://$xrds');
function Auth_Yadis_getNSMap()
{
return array('xrds' => Auth_Yadis_XMLNS_XRDS,
'xrd' => Auth_Yadis_XMLNS_XRD_2_0);
}
/**
* @access private
*/
function Auth_Yadis_array_scramble($arr)
{
$result = array();
while (count($arr)) {
$index = array_rand($arr, 1);
$result[] = $arr[$index];
unset($arr[$index]);
}
return $result;
}
/**
* This class represents a <Service> element in an XRDS document.
* Objects of this type are returned by
* Auth_Yadis_XRDS::services() and
* Auth_Yadis_Yadis::services(). Each object corresponds directly
* to a <Service> element in the XRDS and supplies a
* getElements($name) method which you should use to inspect the
* element's contents. See {@link Auth_Yadis_Yadis} for more
* information on the role this class plays in Yadis discovery.
*
* @package OpenID
*/
class Auth_Yadis_Service {
/**
* Creates an empty service object.
*/
function Auth_Yadis_Service()
{
$this->element = null;
$this->parser = null;
}
/**
* Return the URIs in the "Type" elements, if any, of this Service
* element.
*
* @return array $type_uris An array of Type URI strings.
*/
function getTypes()
{
$t = array();
foreach ($this->getElements('xrd:Type') as $elem) {
$c = $this->parser->content($elem);
if ($c) {
$t[] = $c;
}
}
return $t;
}
function matchTypes($type_uris)
{
$result = array();
foreach ($this->getTypes() as $typ) {
if (in_array($typ, $type_uris)) {
$result[] = $typ;
}
}
return $result;
}
/**
* Return the URIs in the "URI" elements, if any, of this Service
* element. The URIs are returned sorted in priority order.
*
* @return array $uris An array of URI strings.
*/
function getURIs()
{
$uris = array();
$last = array();
foreach ($this->getElements('xrd:URI') as $elem) {
$uri_string = $this->parser->content($elem);
$attrs = $this->parser->attributes($elem);
if ($attrs &&
array_key_exists('priority', $attrs)) {
$priority = intval($attrs['priority']);
if (!array_key_exists($priority, $uris)) {
$uris[$priority] = array();
}
$uris[$priority][] = $uri_string;
} else {
$last[] = $uri_string;
}
}
$keys = array_keys($uris);
sort($keys);
// Rebuild array of URIs.
$result = array();
foreach ($keys as $k) {
$new_uris = Auth_Yadis_array_scramble($uris[$k]);
$result = array_merge($result, $new_uris);
}
$result = array_merge($result,
Auth_Yadis_array_scramble($last));
return $result;
}
/**
* Returns the "priority" attribute value of this <Service>
* element, if the attribute is present. Returns null if not.
*
* @return mixed $result Null or integer, depending on whether
* this Service element has a 'priority' attribute.
*/
function getPriority()
{
$attributes = $this->parser->attributes($this->element);
if (array_key_exists('priority', $attributes)) {
return intval($attributes['priority']);
}
return null;
}
/**
* Used to get XML elements from this object's <Service> element.
*
* This is what you should use to get all custom information out
* of this element. This is used by service filter functions to
* determine whether a service element contains specific tags,
* etc. NOTE: this only considers elements which are direct
* children of the <Service> element for this object.
*
* @param string $name The name of the element to look for
* @return array $list An array of elements with the specified
* name which are direct children of the <Service> element. The
* nodes returned by this function can be passed to $this->parser
* methods (see {@link Auth_Yadis_XMLParser}).
*/
function getElements($name)
{
return $this->parser->evalXPath($name, $this->element);
}
}
/*
* Return the expiration date of this XRD element, or None if no
* expiration was specified.
*
* @param $default The value to use as the expiration if no expiration
* was specified in the XRD.
*/
function Auth_Yadis_getXRDExpiration($xrd_element, $default=null)
{
$expires_element = $xrd_element->$parser->evalXPath('/xrd:Expires');
if ($expires_element === null) {
return $default;
} else {
$expires_string = $expires_element->text;
// Will raise ValueError if the string is not the expected
// format
$t = strptime($expires_string, "%Y-%m-%dT%H:%M:%SZ");
if ($t === false) {
return false;
}
// [int $hour [, int $minute [, int $second [,
// int $month [, int $day [, int $year ]]]]]]
return mktime($t['tm_hour'], $t['tm_min'], $t['tm_sec'],
$t['tm_mon'], $t['tm_day'], $t['tm_year']);
}
}
/**
* This class performs parsing of XRDS documents.
*
* You should not instantiate this class directly; rather, call
* parseXRDS statically:
*
* <pre> $xrds = Auth_Yadis_XRDS::parseXRDS($xml_string);</pre>
*
* If the XRDS can be parsed and is valid, an instance of
* Auth_Yadis_XRDS will be returned. Otherwise, null will be
* returned. This class is used by the Auth_Yadis_Yadis::discover
* method.
*
* @package OpenID
*/
class Auth_Yadis_XRDS {
/**
* Instantiate a Auth_Yadis_XRDS object. Requires an XPath
* instance which has been used to parse a valid XRDS document.
*/
function Auth_Yadis_XRDS($xmlParser, $xrdNodes)
{
$this->parser = $xmlParser;
$this->xrdNode = $xrdNodes[count($xrdNodes) - 1];
$this->allXrdNodes = $xrdNodes;
$this->serviceList = array();
$this->_parse();
}
/**
* Parse an XML string (XRDS document) and return either a
* Auth_Yadis_XRDS object or null, depending on whether the
* XRDS XML is valid.
*
* @param string $xml_string An XRDS XML string.
* @return mixed $xrds An instance of Auth_Yadis_XRDS or null,
* depending on the validity of $xml_string
*/
static function parseXRDS($xml_string, $extra_ns_map = null)
{
$_null = null;
if (!$xml_string) {
return $_null;
}
$parser = Auth_Yadis_getXMLParser();
$ns_map = Auth_Yadis_getNSMap();
if ($extra_ns_map && is_array($extra_ns_map)) {
$ns_map = array_merge($ns_map, $extra_ns_map);
}
if (!($parser && $parser->init($xml_string, $ns_map))) {
return $_null;
}
// Try to get root element.
$root = $parser->evalXPath('/xrds:XRDS[1]');
if (!$root) {
return $_null;
}
if (is_array($root)) {
$root = $root[0];
}
$attrs = $parser->attributes($root);
if (array_key_exists('xmlns:xrd', $attrs) &&
$attrs['xmlns:xrd'] != Auth_Yadis_XMLNS_XRDS) {
return $_null;
} else if (array_key_exists('xmlns', $attrs) &&
preg_match('/xri/', $attrs['xmlns']) &&
$attrs['xmlns'] != Auth_Yadis_XMLNS_XRD_2_0) {
return $_null;
}
// Get the last XRD node.
$xrd_nodes = $parser->evalXPath('/xrds:XRDS[1]/xrd:XRD');
if (!$xrd_nodes) {
return $_null;
}
$xrds = new Auth_Yadis_XRDS($parser, $xrd_nodes);
return $xrds;
}
/**
* @access private
*/
function _addService($priority, $service)
{
$priority = intval($priority);
if (!array_key_exists($priority, $this->serviceList)) {
$this->serviceList[$priority] = array();
}
$this->serviceList[$priority][] = $service;
}
/**
* Creates the service list using nodes from the XRDS XML
* document.
*
* @access private
*/
function _parse()
{
$this->serviceList = array();
$services = $this->parser->evalXPath('xrd:Service', $this->xrdNode);
foreach ($services as $node) {
$s = new Auth_Yadis_Service();
$s->element = $node;
$s->parser = $this->parser;
$priority = $s->getPriority();
if ($priority === null) {
$priority = SERVICES_YADIS_MAX_PRIORITY;
}
$this->_addService($priority, $s);
}
}
/**
* Returns a list of service objects which correspond to <Service>
* elements in the XRDS XML document for this object.
*
* Optionally, an array of filter callbacks may be given to limit
* the list of returned service objects. Furthermore, the default
* mode is to return all service objects which match ANY of the
* specified filters, but $filter_mode may be
* SERVICES_YADIS_MATCH_ALL if you want to be sure that the
* returned services match all the given filters. See {@link
* Auth_Yadis_Yadis} for detailed usage information on filter
* functions.
*
* @param mixed $filters An array of callbacks to filter the
* returned services, or null if all services are to be returned.
* @param integer $filter_mode SERVICES_YADIS_MATCH_ALL or
* SERVICES_YADIS_MATCH_ANY, depending on whether the returned
* services should match ALL or ANY of the specified filters,
* respectively.
* @return mixed $services An array of {@link
* Auth_Yadis_Service} objects if $filter_mode is a valid
* mode; null if $filter_mode is an invalid mode (i.e., not
* SERVICES_YADIS_MATCH_ANY or SERVICES_YADIS_MATCH_ALL).
*/
function services($filters = null,
$filter_mode = SERVICES_YADIS_MATCH_ANY)
{
$pri_keys = array_keys($this->serviceList);
sort($pri_keys, SORT_NUMERIC);
// If no filters are specified, return the entire service
// list, ordered by priority.
if (!$filters ||
(!is_array($filters))) {
$result = array();
foreach ($pri_keys as $pri) {
$result = array_merge($result, $this->serviceList[$pri]);
}
return $result;
}
// If a bad filter mode is specified, return null.
if (!in_array($filter_mode, array(SERVICES_YADIS_MATCH_ANY,
SERVICES_YADIS_MATCH_ALL))) {
return null;
}
// Otherwise, use the callbacks in the filter list to
// determine which services are returned.
$filtered = array();
foreach ($pri_keys as $priority_value) {
$service_obj_list = $this->serviceList[$priority_value];
foreach ($service_obj_list as $service) {
$matches = 0;
foreach ($filters as $filter) {
if (call_user_func_array($filter, array($service))) {
$matches++;
if ($filter_mode == SERVICES_YADIS_MATCH_ANY) {
$pri = $service->getPriority();
if ($pri === null) {
$pri = SERVICES_YADIS_MAX_PRIORITY;
}
if (!array_key_exists($pri, $filtered)) {
$filtered[$pri] = array();
}
$filtered[$pri][] = $service;
break;
}
}
}
if (($filter_mode == SERVICES_YADIS_MATCH_ALL) &&
($matches == count($filters))) {
$pri = $service->getPriority();
if ($pri === null) {
$pri = SERVICES_YADIS_MAX_PRIORITY;
}
if (!array_key_exists($pri, $filtered)) {
$filtered[$pri] = array();
}
$filtered[$pri][] = $service;
}
}
}
$pri_keys = array_keys($filtered);
sort($pri_keys, SORT_NUMERIC);
$result = array();
foreach ($pri_keys as $pri) {
$result = array_merge($result, $filtered[$pri]);
}
return $result;
}
}

View file

@ -1,234 +0,0 @@
<?php
/**
* Routines for XRI resolution.
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005-2008 Janrain, Inc.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
*/
require_once 'Auth/Yadis/Misc.php';
require_once 'Auth/Yadis/Yadis.php';
require_once 'Auth/OpenID.php';
function Auth_Yadis_getDefaultProxy()
{
return 'http://xri.net/';
}
function Auth_Yadis_getXRIAuthorities()
{
return array('!', '=', '@', '+', '$', '(');
}
function Auth_Yadis_getEscapeRE()
{
$parts = array();
foreach (array_merge(Auth_Yadis_getUCSChars(),
Auth_Yadis_getIPrivateChars()) as $pair) {
list($m, $n) = $pair;
$parts[] = sprintf("%s-%s", chr($m), chr($n));
}
return sprintf('/[%s]/', implode('', $parts));
}
function Auth_Yadis_getXrefRE()
{
return '/\((.*?)\)/';
}
function Auth_Yadis_identifierScheme($identifier)
{
if (Auth_Yadis_startswith($identifier, 'xri://') ||
($identifier &&
in_array($identifier[0], Auth_Yadis_getXRIAuthorities()))) {
return "XRI";
} else {
return "URI";
}
}
function Auth_Yadis_toIRINormal($xri)
{
if (!Auth_Yadis_startswith($xri, 'xri://')) {
$xri = 'xri://' . $xri;
}
return Auth_Yadis_escapeForIRI($xri);
}
function _escape_xref($xref_match)
{
$xref = $xref_match[0];
$xref = str_replace('/', '%2F', $xref);
$xref = str_replace('?', '%3F', $xref);
$xref = str_replace('#', '%23', $xref);
return $xref;
}
function Auth_Yadis_escapeForIRI($xri)
{
$xri = str_replace('%', '%25', $xri);
$xri = preg_replace_callback(Auth_Yadis_getXrefRE(),
'_escape_xref', $xri);
return $xri;
}
function Auth_Yadis_toURINormal($xri)
{
return Auth_Yadis_iriToURI(Auth_Yadis_toIRINormal($xri));
}
function Auth_Yadis_iriToURI($iri)
{
if (1) {
return $iri;
} else {
// According to RFC 3987, section 3.1, "Mapping of IRIs to URIs"
return preg_replace_callback(Auth_Yadis_getEscapeRE(),
'Auth_Yadis_pct_escape_unicode', $iri);
}
}
function Auth_Yadis_XRIAppendArgs($url, $args)
{
// Append some arguments to an HTTP query. Yes, this is just like
// OpenID's appendArgs, but with special seasoning for XRI
// queries.
if (count($args) == 0) {
return $url;
}
// Non-empty array; if it is an array of arrays, use multisort;
// otherwise use sort.
if (array_key_exists(0, $args) &&
is_array($args[0])) {
// Do nothing here.
} else {
$keys = array_keys($args);
sort($keys);
$new_args = array();
foreach ($keys as $key) {
$new_args[] = array($key, $args[$key]);
}
$args = $new_args;
}
// According to XRI Resolution section "QXRI query parameters":
//
// "If the original QXRI had a null query component (only a
// leading question mark), or a query component consisting of
// only question marks, one additional leading question mark MUST
// be added when adding any XRI resolution parameters."
if (strpos(rtrim($url, '?'), '?') !== false) {
$sep = '&';
} else {
$sep = '?';
}
return $url . $sep . Auth_OpenID::httpBuildQuery($args);
}
function Auth_Yadis_providerIsAuthoritative($providerID, $canonicalID)
{
$lastbang = strrpos($canonicalID, '!');
$p = substr($canonicalID, 0, $lastbang);
return $p == $providerID;
}
function Auth_Yadis_rootAuthority($xri)
{
// Return the root authority for an XRI.
$root = null;
if (Auth_Yadis_startswith($xri, 'xri://')) {
$xri = substr($xri, 6);
}
$authority = explode('/', $xri, 2);
$authority = $authority[0];
if ($authority[0] == '(') {
// Cross-reference.
// XXX: This is incorrect if someone nests cross-references so
// there is another close-paren in there. Hopefully nobody
// does that before we have a real xriparse function.
// Hopefully nobody does that *ever*.
$root = substr($authority, 0, strpos($authority, ')') + 1);
} else if (in_array($authority[0], Auth_Yadis_getXRIAuthorities())) {
// Other XRI reference.
$root = $authority[0];
} else {
// IRI reference.
$_segments = explode("!", $authority);
$segments = array();
foreach ($_segments as $s) {
$segments = array_merge($segments, explode("*", $s));
}
$root = $segments[0];
}
return Auth_Yadis_XRI($root);
}
function Auth_Yadis_XRI($xri)
{
if (!Auth_Yadis_startswith($xri, 'xri://')) {
$xri = 'xri://' . $xri;
}
return $xri;
}
function Auth_Yadis_getCanonicalID($iname, $xrds)
{
// Returns false or a canonical ID value.
// Now nodes are in reverse order.
$xrd_list = array_reverse($xrds->allXrdNodes);
$parser = $xrds->parser;
$node = $xrd_list[0];
$canonicalID_nodes = $parser->evalXPath('xrd:CanonicalID', $node);
if (!$canonicalID_nodes) {
return false;
}
$canonicalID = $canonicalID_nodes[0];
$canonicalID = Auth_Yadis_XRI($parser->content($canonicalID));
$childID = $canonicalID;
for ($i = 1; $i < count($xrd_list); $i++) {
$xrd = $xrd_list[$i];
$parent_sought = substr($childID, 0, strrpos($childID, '!'));
$parentCID = $parser->evalXPath('xrd:CanonicalID', $xrd);
if (!$parentCID) {
return false;
}
$parentCID = Auth_Yadis_XRI($parser->content($parentCID[0]));
if (strcasecmp($parent_sought, $parentCID)) {
// raise XRDSFraud.
return false;
}
$childID = $parent_sought;
}
$root = Auth_Yadis_rootAuthority($iname);
if (!Auth_Yadis_providerIsAuthoritative($root, $childID)) {
// raise XRDSFraud.
return false;
}
return $canonicalID;
}

View file

@ -1,72 +0,0 @@
<?php
/**
* Code for using a proxy XRI resolver.
*/
require_once 'Auth/Yadis/XRDS.php';
require_once 'Auth/Yadis/XRI.php';
class Auth_Yadis_ProxyResolver {
function Auth_Yadis_ProxyResolver($fetcher, $proxy_url = null)
{
$this->fetcher = $fetcher;
$this->proxy_url = $proxy_url;
if (!$this->proxy_url) {
$this->proxy_url = Auth_Yadis_getDefaultProxy();
}
}
function queryURL($xri, $service_type = null)
{
// trim off the xri:// prefix
$qxri = substr(Auth_Yadis_toURINormal($xri), 6);
$hxri = $this->proxy_url . $qxri;
$args = array(
'_xrd_r' => 'application/xrds+xml'
);
if ($service_type) {
$args['_xrd_t'] = $service_type;
} else {
// Don't perform service endpoint selection.
$args['_xrd_r'] .= ';sep=false';
}
$query = Auth_Yadis_XRIAppendArgs($hxri, $args);
return $query;
}
function query($xri, $service_types, $filters = array())
{
$services = array();
$canonicalID = null;
foreach ($service_types as $service_type) {
$url = $this->queryURL($xri, $service_type);
$response = $this->fetcher->get($url);
if ($response->status != 200 and $response->status != 206) {
continue;
}
$xrds = Auth_Yadis_XRDS::parseXRDS($response->body);
if (!$xrds) {
continue;
}
$canonicalID = Auth_Yadis_getCanonicalID($xri,
$xrds);
if ($canonicalID === false) {
return null;
}
$some_services = $xrds->services($filters);
$services = array_merge($services, $some_services);
// TODO:
// * If we do get hits for multiple service_types, we're
// almost certainly going to have duplicated service
// entries and broken priority ordering.
}
return array($canonicalID, $services);
}
}

View file

@ -1,382 +0,0 @@
<?php
/**
* The core PHP Yadis implementation.
*
* PHP versions 4 and 5
*
* LICENSE: See the COPYING file included in this distribution.
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005-2008 Janrain, Inc.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
*/
/**
* Need both fetcher types so we can use the right one based on the
* presence or absence of CURL.
*/
require_once "Auth/Yadis/PlainHTTPFetcher.php";
require_once "Auth/Yadis/ParanoidHTTPFetcher.php";
/**
* Need this for parsing HTML (looking for META tags).
*/
require_once "Auth/Yadis/ParseHTML.php";
/**
* Need this to parse the XRDS document during Yadis discovery.
*/
require_once "Auth/Yadis/XRDS.php";
/**
* XRDS (yadis) content type
*/
define('Auth_Yadis_CONTENT_TYPE', 'application/xrds+xml');
/**
* Yadis header
*/
define('Auth_Yadis_HEADER_NAME', 'X-XRDS-Location');
/**
* Contains the result of performing Yadis discovery on a URI.
*
* @package OpenID
*/
class Auth_Yadis_DiscoveryResult {
// The URI that was passed to the fetcher
var $request_uri = null;
// The result of following redirects from the request_uri
var $normalized_uri = null;
// The URI from which the response text was returned (set to
// None if there was no XRDS document found)
var $xrds_uri = null;
var $xrds = null;
// The content-type returned with the response_text
var $content_type = null;
// The document returned from the xrds_uri
var $response_text = null;
// Did the discovery fail miserably?
var $failed = false;
function Auth_Yadis_DiscoveryResult($request_uri)
{
// Initialize the state of the object
// sets all attributes to None except the request_uri
$this->request_uri = $request_uri;
}
function fail()
{
$this->failed = true;
}
function isFailure()
{
return $this->failed;
}
/**
* Returns the list of service objects as described by the XRDS
* document, if this yadis object represents a successful Yadis
* discovery.
*
* @return array $services An array of {@link Auth_Yadis_Service}
* objects
*/
function services()
{
if ($this->xrds) {
return $this->xrds->services();
}
return null;
}
function usedYadisLocation()
{
// Was the Yadis protocol's indirection used?
return ($this->xrds_uri && $this->normalized_uri != $this->xrds_uri);
}
function isXRDS()
{
// Is the response text supposed to be an XRDS document?
return ($this->usedYadisLocation() ||
$this->content_type == Auth_Yadis_CONTENT_TYPE);
}
}
/**
*
* Perform the Yadis protocol on the input URL and return an iterable
* of resulting endpoint objects.
*
* input_url: The URL on which to perform the Yadis protocol
*
* @return: The normalized identity URL and an iterable of endpoint
* objects generated by the filter function.
*
* xrds_parse_func: a callback which will take (uri, xrds_text) and
* return an array of service endpoint objects or null. Usually
* array('Auth_OpenID_ServiceEndpoint', 'fromXRDS').
*
* discover_func: if not null, a callback which should take (uri) and
* return an Auth_Yadis_Yadis object or null.
*/
function Auth_Yadis_getServiceEndpoints($input_url, $xrds_parse_func,
$discover_func=null, $fetcher=null)
{
if ($discover_func === null) {
$discover_function = array('Auth_Yadis_Yadis', 'discover');
}
$yadis_result = call_user_func_array($discover_func,
array($input_url, $fetcher));
if ($yadis_result === null) {
return array($input_url, array());
}
$endpoints = call_user_func_array($xrds_parse_func,
array($yadis_result->normalized_uri,
$yadis_result->response_text));
if ($endpoints === null) {
$endpoints = array();
}
return array($yadis_result->normalized_uri, $endpoints);
}
/**
* This is the core of the PHP Yadis library. This is the only class
* a user needs to use to perform Yadis discovery. This class
* performs the discovery AND stores the result of the discovery.
*
* First, require this library into your program source:
*
* <pre> require_once "Auth/Yadis/Yadis.php";</pre>
*
* To perform Yadis discovery, first call the "discover" method
* statically with a URI parameter:
*
* <pre> $http_response = array();
* $fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
* $yadis_object = Auth_Yadis_Yadis::discover($uri,
* $http_response, $fetcher);</pre>
*
* If the discovery succeeds, $yadis_object will be an instance of
* {@link Auth_Yadis_Yadis}. If not, it will be null. The XRDS
* document found during discovery should have service descriptions,
* which can be accessed by calling
*
* <pre> $service_list = $yadis_object->services();</pre>
*
* which returns an array of objects which describe each service.
* These objects are instances of Auth_Yadis_Service. Each object
* describes exactly one whole Service element, complete with all of
* its Types and URIs (no expansion is performed). The common use
* case for using the service objects returned by services() is to
* write one or more filter functions and pass those to services():
*
* <pre> $service_list = $yadis_object->services(
* array("filterByURI",
* "filterByExtension"));</pre>
*
* The filter functions (whose names appear in the array passed to
* services()) take the following form:
*
* <pre> function myFilter($service) {
* // Query $service object here. Return true if the service
* // matches your query; false if not.
* }</pre>
*
* This is an example of a filter which uses a regular expression to
* match the content of URI tags (note that the Auth_Yadis_Service
* class provides a getURIs() method which you should use instead of
* this contrived example):
*
* <pre>
* function URIMatcher($service) {
* foreach ($service->getElements('xrd:URI') as $uri) {
* if (preg_match("/some_pattern/",
* $service->parser->content($uri))) {
* return true;
* }
* }
* return false;
* }</pre>
*
* The filter functions you pass will be called for each service
* object to determine which ones match the criteria your filters
* specify. The default behavior is that if a given service object
* matches ANY of the filters specified in the services() call, it
* will be returned. You can specify that a given service object will
* be returned ONLY if it matches ALL specified filters by changing
* the match mode of services():
*
* <pre> $yadis_object->services(array("filter1", "filter2"),
* SERVICES_YADIS_MATCH_ALL);</pre>
*
* See {@link SERVICES_YADIS_MATCH_ALL} and {@link
* SERVICES_YADIS_MATCH_ANY}.
*
* Services described in an XRDS should have a library which you'll
* probably be using. Those libraries are responsible for defining
* filters that can be used with the "services()" call. If you need
* to write your own filter, see the documentation for {@link
* Auth_Yadis_Service}.
*
* @package OpenID
*/
class Auth_Yadis_Yadis {
/**
* Returns an HTTP fetcher object. If the CURL extension is
* present, an instance of {@link Auth_Yadis_ParanoidHTTPFetcher}
* is returned. If not, an instance of
* {@link Auth_Yadis_PlainHTTPFetcher} is returned.
*
* If Auth_Yadis_CURL_OVERRIDE is defined, this method will always
* return a {@link Auth_Yadis_PlainHTTPFetcher}.
*/
static function getHTTPFetcher($timeout = 20)
{
if (Auth_Yadis_Yadis::curlPresent() &&
(!defined('Auth_Yadis_CURL_OVERRIDE'))) {
$fetcher = new Auth_Yadis_ParanoidHTTPFetcher($timeout);
} else {
$fetcher = new Auth_Yadis_PlainHTTPFetcher($timeout);
}
return $fetcher;
}
static function curlPresent()
{
return function_exists('curl_init');
}
/**
* @access private
*/
static function _getHeader($header_list, $names)
{
foreach ($header_list as $name => $value) {
foreach ($names as $n) {
if (strtolower($name) == strtolower($n)) {
return $value;
}
}
}
return null;
}
/**
* @access private
*/
static function _getContentType($content_type_header)
{
if ($content_type_header) {
$parts = explode(";", $content_type_header);
return strtolower($parts[0]);
}
}
/**
* This should be called statically and will build a Yadis
* instance if the discovery process succeeds. This implements
* Yadis discovery as specified in the Yadis specification.
*
* @param string $uri The URI on which to perform Yadis discovery.
*
* @param array $http_response An array reference where the HTTP
* response object will be stored (see {@link
* Auth_Yadis_HTTPResponse}.
*
* @param Auth_Yadis_HTTPFetcher $fetcher An instance of a
* Auth_Yadis_HTTPFetcher subclass.
*
* @param array $extra_ns_map An array which maps namespace names
* to namespace URIs to be used when parsing the Yadis XRDS
* document.
*
* @param integer $timeout An optional fetcher timeout, in seconds.
*
* @return mixed $obj Either null or an instance of
* Auth_Yadis_Yadis, depending on whether the discovery
* succeeded.
*/
static function discover($uri, $fetcher,
$extra_ns_map = null, $timeout = 20)
{
$result = new Auth_Yadis_DiscoveryResult($uri);
$request_uri = $uri;
$headers = array("Accept: " . Auth_Yadis_CONTENT_TYPE .
', text/html; q=0.3, application/xhtml+xml; q=0.5');
if ($fetcher === null) {
$fetcher = Auth_Yadis_Yadis::getHTTPFetcher($timeout);
}
$response = $fetcher->get($uri, $headers);
if (!$response || ($response->status != 200 and
$response->status != 206)) {
$result->fail();
return $result;
}
$result->normalized_uri = $response->final_url;
$result->content_type = Auth_Yadis_Yadis::_getHeader(
$response->headers,
array('content-type'));
if ($result->content_type &&
(Auth_Yadis_Yadis::_getContentType($result->content_type) ==
Auth_Yadis_CONTENT_TYPE)) {
$result->xrds_uri = $result->normalized_uri;
} else {
$yadis_location = Auth_Yadis_Yadis::_getHeader(
$response->headers,
array(Auth_Yadis_HEADER_NAME));
if (!$yadis_location) {
$parser = new Auth_Yadis_ParseHTML();
$yadis_location = $parser->getHTTPEquiv($response->body);
}
if ($yadis_location) {
$result->xrds_uri = $yadis_location;
$response = $fetcher->get($yadis_location);
if ((!$response) || ($response->status != 200 and
$response->status != 206)) {
$result->fail();
return $result;
}
$result->content_type = Auth_Yadis_Yadis::_getHeader(
$response->headers,
array('content-type'));
}
}
$result->response_text = $response->body;
return $result;
}
}

View file

@ -1,238 +0,0 @@
<?php
namespace Dropbox;
/**
* Your app's API key and secret.
*/
final class AppInfo
{
/**
* Your Dropbox <em>app key</em> (OAuth calls this the <em>consumer key</em>). You can
* create an app key and secret on the <a href="http://dropbox.com/developers/apps">Dropbox developer website</a>.
*
* @return string
*/
function getKey() { return $this->key; }
/** @var string */
private $key;
/**
* Your Dropbox <em>app secret</em> (OAuth calls this the <em>consumer secret</em>). You can
* create an app key and secret on the <a href="http://dropbox.com/developers/apps">Dropbox developer website</a>.
*
* Make sure that this is kept a secret. Someone with your app secret can impesonate your
* application. People sometimes ask for help on the Dropbox API forums and
* copy/paste code that includes their app secret. Do not do that.
*
* @return string
*/
function getSecret() { return $this->secret; }
/** @var string */
private $secret;
/**
* The set of servers your app will use. This defaults to the standard Dropbox servers
* {@link Host::getDefault}.
*
* @return Host
*
* @internal
*/
function getHost() { return $this->host; }
/** @var Host */
private $host;
/**
* Constructor.
*
* @param string $key
* See {@link getKey()}
* @param string $secret
* See {@link getSecret()}
*/
function __construct($key, $secret)
{
self::checkKeyArg($key);
self::checkSecretArg($secret);
$this->key = $key;
$this->secret = $secret;
// The $host parameter is sort of internal. We don't include it in the param list because
// we don't want it to be included in the documentation. Use PHP arg list hacks to get at
// it.
$host = null;
if (\func_num_args() == 3) {
$host = \func_get_arg(2);
Host::checkArgOrNull("host", $host);
}
if ($host === null) {
$host = Host::getDefault();
}
$this->host = $host;
}
/**
* Loads a JSON file containing information about your app. At a minimum, the file must include
* the "key" and "secret" fields. Run 'php authorize.php' in the examples directory
* for details about what this file should look like.
*
* @param string $path
* Path to a JSON file
*
* @return AppInfo
*
* @throws AppInfoLoadException
*/
static function loadFromJsonFile($path)
{
list($rawJson, $appInfo) = self::loadFromJsonFileWithRaw($path);
return $appInfo;
}
/**
* Loads a JSON file containing information about your app. At a minimum, the file must include
* the "key" and "secret" fields. Run 'php authorize.php' in the examples directory
* for details about what this file should look like.
*
* @param string $path
* Path to a JSON file
*
* @return array
* A list of two items. The first is a PHP array representation of the raw JSON, the second
* is an AppInfo object that is the parsed version of the JSON.
*
* @throws AppInfoLoadException
*
* @internal
*/
static function loadFromJsonFileWithRaw($path)
{
if (!file_exists($path)) {
throw new AppInfoLoadException("File doesn't exist: \"$path\"");
}
$str = file_get_contents($path);
$jsonArr = json_decode($str, true);
if (is_null($jsonArr)) {
throw new AppInfoLoadException("JSON parse error: \"$path\"");
}
$appInfo = self::loadFromJson($jsonArr);
return array($jsonArr, $appInfo);
}
/**
* Parses a JSON object to build an AppInfo object. If you would like to load this from a file,
* use the loadFromJsonFile() method.
*
* @param array $jsonArr Output from json_decode($str, true)
*
* @return AppInfo
*
* @throws AppInfoLoadException
*/
static function loadFromJson($jsonArr)
{
if (!is_array($jsonArr)) {
throw new AppInfoLoadException("Expecting JSON object, got something else");
}
$requiredKeys = array("key", "secret");
foreach ($requiredKeys as $key) {
if (!array_key_exists($key, $jsonArr)) {
throw new AppInfoLoadException("Missing field \"$key\"");
}
if (!is_string($jsonArr[$key])) {
throw new AppInfoLoadException("Expecting field \"$key\" to be a string");
}
}
// Check app_key and app_secret
$appKey = $jsonArr["key"];
$appSecret = $jsonArr["secret"];
$tokenErr = self::getTokenPartError($appKey);
if (!is_null($tokenErr)) {
throw new AppInfoLoadException("Field \"key\" doesn't look like a valid app key: $tokenErr");
}
$tokenErr = self::getTokenPartError($appSecret);
if (!is_null($tokenErr)) {
throw new AppInfoLoadException("Field \"secret\" doesn't look like a valid app secret: $tokenErr");
}
// Check for the optional 'host' field
if (!array_key_exists('host', $jsonArr)) {
$host = null;
}
else {
$baseHost = $jsonArr["host"];
if (!is_string($baseHost)) {
throw new AppInfoLoadException("Optional field \"host\" must be a string");
}
$api = "api-$baseHost";
$content = "api-content-$baseHost";
$web = "meta-$baseHost";
$host = new Host($api, $content, $web);
}
return new AppInfo($appKey, $appSecret, $host);
}
/**
* Use this to check that a function argument is of type <code>AppInfo</code>
*
* @internal
*/
static function checkArg($argName, $argValue)
{
if (!($argValue instanceof self)) Checker::throwError($argName, $argValue, __CLASS__);
}
/**
* Use this to check that a function argument is either <code>null</code> or of type
* <code>AppInfo</code>.
*
* @internal
*/
static function checkArgOrNull($argName, $argValue)
{
if ($argValue === null) return;
if (!($argValue instanceof self)) Checker::throwError($argName, $argValue, __CLASS__);
}
/** @internal */
static function getTokenPartError($s)
{
if ($s === null) return "can't be null";
if (strlen($s) === 0) return "can't be empty";
if (strstr($s, ' ')) return "can't contain a space";
return null; // 'null' means "no error"
}
/** @internal */
static function checkKeyArg($key)
{
$error = self::getTokenPartError($key);
if ($error === null) return;
throw new \InvalidArgumentException("Bad 'key': \"$key\": $error.");
}
/** @internal */
static function checkSecretArg($secret)
{
$error = self::getTokenPartError($secret);
if ($error === null) return;
throw new \InvalidArgumentException("Bad 'secret': \"$secret\": $error.");
}
}

Some files were not shown because too many files have changed in this diff Show more